1 #include "first.h" 2 3 #include <string.h> 4 #include <stdlib.h> 5 6 #include "gw_backend.h" 7 #include "base.h" 8 #include "array.h" 9 #include "buffer.h" 10 #include "fdevent.h" 11 #include "http_kv.h" 12 #include "http_header.h" 13 #include "log.h" 14 #include "sock_addr.h" 15 #include "status_counter.h" 16 17 /** 18 * 19 * HTTP reverse proxy 20 * 21 * TODO: - HTTP/1.1 22 * - HTTP/1.1 persistent connection with upstream servers 23 */ 24 25 /* (future: might split struct and move part to http-header-glue.c) */ 26 typedef struct http_header_remap_opts { 27 const array *urlpaths; 28 const array *hosts_request; 29 const array *hosts_response; 30 int https_remap; 31 int upgrade; 32 int connect_method; 33 /*(not used in plugin_config, but used in handler_ctx)*/ 34 const buffer *http_host; 35 const buffer *forwarded_host; 36 const data_string *forwarded_urlpath; 37 } http_header_remap_opts; 38 39 typedef enum { 40 PROXY_FORWARDED_NONE = 0x00, 41 PROXY_FORWARDED_FOR = 0x01, 42 PROXY_FORWARDED_PROTO = 0x02, 43 PROXY_FORWARDED_HOST = 0x04, 44 PROXY_FORWARDED_BY = 0x08, 45 PROXY_FORWARDED_REMOTE_USER = 0x10 46 } proxy_forwarded_t; 47 48 typedef struct { 49 gw_plugin_config gw; /* start must match layout of gw_plugin_config */ 50 unsigned int replace_http_host; 51 unsigned int forwarded; 52 http_header_remap_opts header; 53 } plugin_config; 54 55 typedef struct { 56 PLUGIN_DATA; 57 pid_t srv_pid; /* must match layout of gw_plugin_data through conf member */ 58 plugin_config conf; 59 plugin_config defaults; 60 } plugin_data; 61 62 static int proxy_check_extforward; 63 static int proxy_force_http10; 64 65 typedef struct { 66 gw_handler_ctx gw; 67 http_response_opts opts; 68 plugin_config conf; 69 } handler_ctx; 70 71 72 INIT_FUNC(mod_proxy_init) { 73 return calloc(1, sizeof(plugin_data)); 74 } 75 76 77 static void mod_proxy_free_config(plugin_data * const p) 78 { 79 if (NULL == p->cvlist) return; 80 /* (init i to 0 if global context; to 1 to skip empty global context) */ 81 for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) { 82 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 83 for (; -1 != cpv->k_id; ++cpv) { 84 switch (cpv->k_id) { 85 case 5: /* proxy.header */ 86 if (cpv->vtype == T_CONFIG_LOCAL) free(cpv->v.v); 87 break; 88 default: 89 break; 90 } 91 } 92 } 93 } 94 95 96 FREE_FUNC(mod_proxy_free) { 97 plugin_data * const p = p_d; 98 mod_proxy_free_config(p); 99 gw_free(p); 100 } 101 102 static void mod_proxy_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) 103 { 104 switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ 105 case 0: /* proxy.server */ 106 if (cpv->vtype == T_CONFIG_LOCAL) { 107 gw_plugin_config * const gw = cpv->v.v; 108 pconf->gw.exts = gw->exts; 109 pconf->gw.exts_auth = gw->exts_auth; 110 pconf->gw.exts_resp = gw->exts_resp; 111 } 112 break; 113 case 1: /* proxy.balance */ 114 /*if (cpv->vtype == T_CONFIG_LOCAL)*//*always true here for this param*/ 115 pconf->gw.balance = (int)cpv->v.u; 116 break; 117 case 2: /* proxy.debug */ 118 pconf->gw.debug = (int)cpv->v.u; 119 break; 120 case 3: /* proxy.map-extensions */ 121 pconf->gw.ext_mapping = cpv->v.a; 122 break; 123 case 4: /* proxy.forwarded */ 124 /*if (cpv->vtype == T_CONFIG_LOCAL)*//*always true here for this param*/ 125 pconf->forwarded = cpv->v.u; 126 break; 127 case 5: /* proxy.header */ 128 /*if (cpv->vtype == T_CONFIG_LOCAL)*//*always true here for this param*/ 129 pconf->header = *(http_header_remap_opts *)cpv->v.v; /*(copies struct)*/ 130 break; 131 case 6: /* proxy.replace-http-host */ 132 pconf->replace_http_host = cpv->v.u; 133 break; 134 default:/* should not happen */ 135 return; 136 } 137 } 138 139 140 static void mod_proxy_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) 141 { 142 do { 143 mod_proxy_merge_config_cpv(pconf, cpv); 144 } while ((++cpv)->k_id != -1); 145 } 146 147 148 static void mod_proxy_patch_config(request_st * const r, plugin_data * const p) 149 { 150 memcpy(&p->conf, &p->defaults, sizeof(plugin_config)); 151 for (int i = 1, used = p->nconfig; i < used; ++i) { 152 if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id)) 153 mod_proxy_merge_config(&p->conf, p->cvlist+p->cvlist[i].v.u2[0]); 154 } 155 } 156 157 158 static unsigned int mod_proxy_parse_forwarded(server *srv, const array *a) 159 { 160 unsigned int forwarded = 0; 161 for (uint32_t j = 0, used = a->used; j < used; ++j) { 162 proxy_forwarded_t param; 163 data_unset *du = a->data[j]; 164 if (buffer_eq_slen(&du->key, CONST_STR_LEN("by"))) 165 param = PROXY_FORWARDED_BY; 166 else if (buffer_eq_slen(&du->key, CONST_STR_LEN("for"))) 167 param = PROXY_FORWARDED_FOR; 168 else if (buffer_eq_slen(&du->key, CONST_STR_LEN("host"))) 169 param = PROXY_FORWARDED_HOST; 170 else if (buffer_eq_slen(&du->key, CONST_STR_LEN("proto"))) 171 param = PROXY_FORWARDED_PROTO; 172 else if (buffer_eq_slen(&du->key, CONST_STR_LEN("remote_user"))) 173 param = PROXY_FORWARDED_REMOTE_USER; 174 else { 175 log_error(srv->errh, __FILE__, __LINE__, 176 "proxy.forwarded keys must be one of: " 177 "by, for, host, proto, remote_user, but not: %s", du->key.ptr); 178 return UINT_MAX; 179 } 180 if (du->type == TYPE_STRING) { 181 data_string *ds = (data_string *)du; 182 if (buffer_eq_slen(&ds->value, CONST_STR_LEN("enable"))) 183 forwarded |= param; 184 else if (!buffer_eq_slen(&ds->value, CONST_STR_LEN("disable"))) { 185 log_error(srv->errh, __FILE__, __LINE__, 186 "proxy.forwarded values must be one of: " 187 "0, 1, enable, disable; error for key: %s", du->key.ptr); 188 return UINT_MAX; 189 } 190 } 191 else if (du->type == TYPE_INTEGER) { 192 data_integer *di = (data_integer *)du; 193 if (di->value) forwarded |= param; 194 } 195 else { 196 log_error(srv->errh, __FILE__, __LINE__, 197 "proxy.forwarded values must be one of: " 198 "0, 1, enable, disable; error for key: %s", du->key.ptr); 199 return UINT_MAX; 200 } 201 } 202 return forwarded; 203 } 204 205 206 static http_header_remap_opts * mod_proxy_parse_header_opts(server *srv, const array *a) 207 { 208 http_header_remap_opts header; 209 memset(&header, 0, sizeof(header)); 210 for (uint32_t j = 0, used = a->used; j < used; ++j) { 211 data_array *da = (data_array *)a->data[j]; 212 if (buffer_eq_slen(&da->key, CONST_STR_LEN("https-remap"))) { 213 data_string *ds = (data_string *)da; 214 if (ds->type != TYPE_STRING) { 215 log_error(srv->errh, __FILE__, __LINE__, 216 "unexpected value for proxy.header; " 217 "expected \"enable\" or \"disable\" for https-remap"); 218 return NULL; 219 } 220 header.https_remap = 221 !buffer_eq_slen(&ds->value, CONST_STR_LEN("disable")) 222 && !buffer_eq_slen(&ds->value, CONST_STR_LEN("0")); 223 continue; 224 } 225 else if (buffer_eq_slen(&da->key, CONST_STR_LEN("upgrade"))) { 226 data_string *ds = (data_string *)da; 227 if (ds->type != TYPE_STRING) { 228 log_error(srv->errh, __FILE__, __LINE__, 229 "unexpected value for proxy.header; " 230 "expected \"upgrade\" => \"enable\" or \"disable\""); 231 return NULL; 232 } 233 header.upgrade = 234 !buffer_eq_slen(&ds->value, CONST_STR_LEN("disable")) 235 && !buffer_eq_slen(&ds->value, CONST_STR_LEN("0")); 236 continue; 237 } 238 else if (buffer_eq_slen(&da->key, CONST_STR_LEN("connect"))) { 239 data_string *ds = (data_string *)da; 240 if (ds->type != TYPE_STRING) { 241 log_error(srv->errh, __FILE__, __LINE__, 242 "unexpected value for proxy.header; " 243 "expected \"connect\" => \"enable\" or \"disable\""); 244 return NULL; 245 } 246 header.connect_method = 247 !buffer_eq_slen(&ds->value, CONST_STR_LEN("disable")) 248 && !buffer_eq_slen(&ds->value, CONST_STR_LEN("0")); 249 continue; 250 } 251 if (da->type != TYPE_ARRAY || !array_is_kvstring(&da->value)) { 252 log_error(srv->errh, __FILE__, __LINE__, 253 "unexpected value for proxy.header; " 254 "expected ( \"param\" => ( \"key\" => \"value\" ) ) near key %s", 255 da->key.ptr); 256 return NULL; 257 } 258 if (buffer_eq_slen(&da->key, CONST_STR_LEN("map-urlpath"))) { 259 header.urlpaths = &da->value; 260 } 261 else if (buffer_eq_slen(&da->key, CONST_STR_LEN("map-host-request"))) { 262 header.hosts_request = &da->value; 263 } 264 else if (buffer_eq_slen(&da->key, CONST_STR_LEN("map-host-response"))) { 265 header.hosts_response = &da->value; 266 } 267 else { 268 log_error(srv->errh, __FILE__, __LINE__, 269 "unexpected key for proxy.header; " 270 "expected ( \"param\" => ( \"key\" => \"value\" ) ) near key %s", 271 da->key.ptr); 272 return NULL; 273 } 274 } 275 276 http_header_remap_opts *opts = malloc(sizeof(header)); 277 force_assert(opts); 278 memcpy(opts, &header, sizeof(header)); 279 return opts; 280 } 281 282 283 SETDEFAULTS_FUNC(mod_proxy_set_defaults) 284 { 285 static const config_plugin_keys_t cpk[] = { 286 { CONST_STR_LEN("proxy.server"), 287 T_CONFIG_ARRAY_KVARRAY, 288 T_CONFIG_SCOPE_CONNECTION } 289 ,{ CONST_STR_LEN("proxy.balance"), 290 T_CONFIG_STRING, 291 T_CONFIG_SCOPE_CONNECTION } 292 ,{ CONST_STR_LEN("proxy.debug"), 293 T_CONFIG_INT, 294 T_CONFIG_SCOPE_CONNECTION } 295 ,{ CONST_STR_LEN("proxy.map-extensions"), 296 T_CONFIG_ARRAY_KVSTRING, 297 T_CONFIG_SCOPE_CONNECTION } 298 ,{ CONST_STR_LEN("proxy.forwarded"), 299 T_CONFIG_ARRAY_KVANY, 300 T_CONFIG_SCOPE_CONNECTION } 301 ,{ CONST_STR_LEN("proxy.header"), 302 T_CONFIG_ARRAY_KVANY, 303 T_CONFIG_SCOPE_CONNECTION } 304 ,{ CONST_STR_LEN("proxy.replace-http-host"), 305 T_CONFIG_BOOL, 306 T_CONFIG_SCOPE_CONNECTION } 307 ,{ NULL, 0, 308 T_CONFIG_UNSET, 309 T_CONFIG_SCOPE_UNSET } 310 }; 311 312 plugin_data * const p = p_d; 313 if (!config_plugin_values_init(srv, p, cpk, "mod_proxy")) 314 return HANDLER_ERROR; 315 316 /* process and validate config directives 317 * (init i to 0 if global context; to 1 to skip empty global context) */ 318 for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { 319 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 320 gw_plugin_config *gw = NULL; 321 for (; -1 != cpv->k_id; ++cpv) { 322 switch (cpv->k_id) { 323 case 0: /* proxy.server */ 324 gw = calloc(1, sizeof(gw_plugin_config)); 325 force_assert(gw); 326 if (!gw_set_defaults_backend(srv, (gw_plugin_data *)p, cpv->v.a, 327 gw, 0, cpk[cpv->k_id].k)) { 328 gw_plugin_config_free(gw); 329 return HANDLER_ERROR; 330 } 331 /* error if "mode" = "authorizer"; 332 * proxy can not act as authorizer */ 333 /*(check after gw_set_defaults_backend())*/ 334 if (gw->exts_auth && gw->exts_auth->used) { 335 log_error(srv->errh, __FILE__, __LINE__, 336 "%s must not define any hosts with " 337 "attribute \"mode\" = \"authorizer\"", cpk[cpv->k_id].k); 338 gw_plugin_config_free(gw); 339 return HANDLER_ERROR; 340 } 341 cpv->v.v = gw; 342 cpv->vtype = T_CONFIG_LOCAL; 343 break; 344 case 1: /* proxy.balance */ 345 cpv->v.u = (unsigned int)gw_get_defaults_balance(srv, cpv->v.b); 346 break; 347 case 2: /* proxy.debug */ 348 case 3: /* proxy.map-extensions */ 349 break; 350 case 4: /* proxy.forwarded */ 351 cpv->v.u = mod_proxy_parse_forwarded(srv, cpv->v.a); 352 if (UINT_MAX == cpv->v.u) return HANDLER_ERROR; 353 cpv->vtype = T_CONFIG_LOCAL; 354 break; 355 case 5: /* proxy.header */ 356 cpv->v.v = mod_proxy_parse_header_opts(srv, cpv->v.a); 357 if (NULL == cpv->v.v) return HANDLER_ERROR; 358 cpv->vtype = T_CONFIG_LOCAL; 359 break; 360 case 6: /* proxy.replace-http-host */ 361 break; 362 default:/* should not happen */ 363 break; 364 } 365 } 366 367 /* disable check-local for all exts (default enabled) */ 368 if (gw && gw->exts) { /*(check after gw_set_defaults_backend())*/ 369 gw_exts_clear_check_local(gw->exts); 370 } 371 } 372 373 /* default is 0 */ 374 /*p->defaults.balance = (unsigned int)gw_get_defaults_balance(srv, NULL);*/ 375 376 /* initialize p->defaults from global config context */ 377 if (p->nconfig > 0 && p->cvlist->v.u2[1]) { 378 const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; 379 if (-1 != cpv->k_id) 380 mod_proxy_merge_config(&p->defaults, cpv); 381 } 382 383 /* special-case behavior if mod_extforward is loaded */ 384 for (uint32_t i = 0; i < srv->srvconf.modules->used; ++i) { 385 buffer *m = &((data_string *)srv->srvconf.modules->data[i])->value; 386 if (buffer_eq_slen(m, CONST_STR_LEN("mod_extforward"))) { 387 proxy_check_extforward = 1; 388 break; 389 } 390 } 391 392 proxy_force_http10 = 393 srv->srvconf.feature_flags 394 && config_plugin_value_tobool( 395 array_get_element_klen(srv->srvconf.feature_flags, 396 CONST_STR_LEN("proxy.force-http10")), 0); 397 398 return HANDLER_GO_ON; 399 } 400 401 402 /* (future: might move to http-header-glue.c) */ 403 static const buffer * http_header_remap_host_match (buffer *b, size_t off, http_header_remap_opts *remap_hdrs, int is_req, size_t alen) 404 { 405 const array *hosts = is_req 406 ? remap_hdrs->hosts_request 407 : remap_hdrs->hosts_response; 408 if (hosts) { 409 const char * const s = b->ptr+off; 410 for (size_t i = 0, used = hosts->used; i < used; ++i) { 411 const data_string * const ds = (data_string *)hosts->data[i]; 412 const buffer *k = &ds->key; 413 size_t mlen = buffer_string_length(k); 414 if (1 == mlen && k->ptr[0] == '-') { 415 /* match with authority provided in Host (if is_req) 416 * (If no Host in client request, then matching against empty 417 * string will probably not match, and no remap will be 418 * performed) */ 419 k = is_req 420 ? remap_hdrs->http_host 421 : remap_hdrs->forwarded_host; 422 if (NULL == k) continue; 423 mlen = buffer_string_length(k); 424 } 425 if (buffer_eq_icase_ss(s, alen, k->ptr, mlen)) { 426 if (buffer_is_equal_string(&ds->value, CONST_STR_LEN("-"))) { 427 return remap_hdrs->http_host; 428 } 429 else if (!buffer_string_is_empty(&ds->value)) { 430 /*(save first matched request host for response match)*/ 431 if (is_req && NULL == remap_hdrs->forwarded_host) 432 remap_hdrs->forwarded_host = &ds->value; 433 return &ds->value; 434 } /*(else leave authority as-is and stop matching)*/ 435 break; 436 } 437 } 438 } 439 return NULL; 440 } 441 442 443 /* (future: might move to http-header-glue.c) */ 444 static size_t http_header_remap_host (buffer *b, size_t off, http_header_remap_opts *remap_hdrs, int is_req, size_t alen) 445 { 446 const buffer * const m = 447 http_header_remap_host_match(b, off, remap_hdrs, is_req, alen); 448 if (NULL == m) return alen; /*(no match; return original authority length)*/ 449 450 buffer_substr_replace(b, off, alen, m); 451 return buffer_string_length(m); /*(length of replacement authority)*/ 452 } 453 454 455 /* (future: might move to http-header-glue.c) */ 456 static size_t http_header_remap_urlpath (buffer *b, size_t off, http_header_remap_opts *remap_hdrs, int is_req) 457 { 458 const array *urlpaths = remap_hdrs->urlpaths; 459 if (urlpaths) { 460 const char * const s = b->ptr+off; 461 const size_t plen = buffer_string_length(b) - off; /*(urlpath len)*/ 462 if (is_req) { /* request */ 463 for (size_t i = 0, used = urlpaths->used; i < used; ++i) { 464 const data_string * const ds = (data_string *)urlpaths->data[i]; 465 const size_t mlen = buffer_string_length(&ds->key); 466 if (mlen <= plen && 0 == memcmp(s, ds->key.ptr, mlen)) { 467 if (NULL == remap_hdrs->forwarded_urlpath) 468 remap_hdrs->forwarded_urlpath = ds; 469 buffer_substr_replace(b, off, mlen, &ds->value); 470 return buffer_string_length(&ds->value);/*(replacement len)*/ 471 } 472 } 473 } 474 else { /* response; perform reverse map */ 475 if (NULL != remap_hdrs->forwarded_urlpath) { 476 const data_string * const ds = remap_hdrs->forwarded_urlpath; 477 const size_t mlen = buffer_string_length(&ds->value); 478 if (mlen <= plen && 0 == memcmp(s, ds->value.ptr, mlen)) { 479 buffer_substr_replace(b, off, mlen, &ds->key); 480 return buffer_string_length(&ds->key); /*(replacement len)*/ 481 } 482 } 483 for (size_t i = 0, used = urlpaths->used; i < used; ++i) { 484 const data_string * const ds = (data_string *)urlpaths->data[i]; 485 const size_t mlen = buffer_string_length(&ds->value); 486 if (mlen <= plen && 0 == memcmp(s, ds->value.ptr, mlen)) { 487 buffer_substr_replace(b, off, mlen, &ds->key); 488 return buffer_string_length(&ds->key); /*(replacement len)*/ 489 } 490 } 491 } 492 } 493 return 0; 494 } 495 496 497 /* (future: might move to http-header-glue.c) */ 498 static void http_header_remap_uri (buffer *b, size_t off, http_header_remap_opts *remap_hdrs, int is_req) 499 { 500 /* find beginning of URL-path (might be preceded by scheme://authority 501 * (caller should make sure any leading whitespace is prior to offset) */ 502 if (b->ptr[off] != '/') { 503 char *s = b->ptr+off; 504 size_t alen; /*(authority len (host len))*/ 505 size_t slen; /*(scheme len)*/ 506 const buffer *m; 507 /* skip over scheme and authority of URI to find beginning of URL-path 508 * (value might conceivably be relative URL-path instead of URI) */ 509 if (NULL == (s = strchr(s, ':')) || s[1] != '/' || s[2] != '/') return; 510 slen = s - (b->ptr+off); 511 s += 3; 512 off = (size_t)(s - b->ptr); 513 if (NULL != (s = strchr(s, '/'))) { 514 alen = (size_t)(s - b->ptr) - off; 515 if (0 == alen) return; /*(empty authority, e.g. "http:///")*/ 516 } 517 else { 518 alen = buffer_string_length(b) - off; 519 if (0 == alen) return; /*(empty authority, e.g. "http:///")*/ 520 buffer_append_string_len(b, CONST_STR_LEN("/")); 521 } 522 523 /* remap authority (if configured) and set offset to url-path */ 524 m = http_header_remap_host_match(b, off, remap_hdrs, is_req, alen); 525 if (NULL != m) { 526 if (remap_hdrs->https_remap 527 && (is_req ? 5==slen && 0==memcmp(b->ptr+off-slen-3,"https",5) 528 : 4==slen && 0==memcmp(b->ptr+off-slen-3,"http",4))){ 529 if (is_req) { 530 memcpy(b->ptr+off-slen-3+4,"://",3); /*("https"=>"http")*/ 531 --off; 532 ++alen; 533 } 534 else {/*(!is_req)*/ 535 memcpy(b->ptr+off-slen-3+4,"s://",4); /*("http" =>"https")*/ 536 ++off; 537 --alen; 538 } 539 } 540 buffer_substr_replace(b, off, alen, m); 541 alen = buffer_string_length(m);/*(length of replacement authority)*/ 542 } 543 off += alen; 544 } 545 546 /* remap URLs (if configured) */ 547 http_header_remap_urlpath(b, off, remap_hdrs, is_req); 548 } 549 550 551 /* (future: might move to http-header-glue.c) */ 552 static void http_header_remap_setcookie (buffer *b, size_t off, http_header_remap_opts *remap_hdrs) 553 { 554 /* Given the special-case of Set-Cookie and the (too) loosely restricted 555 * characters allowed, for best results, the Set-Cookie value should be the 556 * entire string in b from offset to end of string. In response headers, 557 * lighttpd may concatenate multiple Set-Cookie headers into single entry 558 * in r->resp_headers, separated by "\r\nSet-Cookie: " */ 559 for (char *s = b->ptr+off, *e; *s; s = e) { 560 size_t len; 561 { 562 while (*s != ';' && *s != '\n' && *s != '\0') ++s; 563 if (*s == '\n') { 564 /*(include +1 for '\n', but leave ' ' for ++s below)*/ 565 s += sizeof("Set-Cookie:"); 566 } 567 if ('\0' == *s) return; 568 do { ++s; } while (*s == ' ' || *s == '\t'); 569 if ('\0' == *s) return; 570 e = s+1; 571 if ('=' == *s) continue; 572 /*(interested only in Domain and Path attributes)*/ 573 while (*e != '=' && *e != '\0') ++e; 574 if ('\0' == *e) return; 575 ++e; 576 switch ((int)(e - s - 1)) { 577 case 4: 578 if (buffer_eq_icase_ssn(s, "path", 4)) { 579 if (*e == '"') ++e; 580 if (*e != '/') continue; 581 off = (size_t)(e - b->ptr); 582 len = http_header_remap_urlpath(b, off, remap_hdrs, 0); 583 e = b->ptr+off+len; /*(b may have been reallocated)*/ 584 continue; 585 } 586 break; 587 case 6: 588 if (buffer_eq_icase_ssn(s, "domain", 6)) { 589 size_t alen = 0; 590 if (*e == '"') ++e; 591 if (*e == '.') ++e; 592 if (*e == ';') continue; 593 off = (size_t)(e - b->ptr); 594 for (char c; (c = e[alen]) != ';' && c != ' ' && c != '\t' 595 && c != '\r' && c != '\0'; ++alen); 596 len = http_header_remap_host(b, off, remap_hdrs, 0, alen); 597 e = b->ptr+off+len; /*(b may have been reallocated)*/ 598 continue; 599 } 600 break; 601 default: 602 break; 603 } 604 } 605 } 606 } 607 608 609 static void buffer_append_string_backslash_escaped(buffer *b, const char *s, size_t len) { 610 /* (future: might move to buffer.c) */ 611 size_t j = 0; 612 char *p; 613 614 buffer_string_prepare_append(b, len*2 + 4); 615 p = b->ptr + buffer_string_length(b); 616 617 for (size_t i = 0; i < len; ++i) { 618 int c = s[i]; 619 if (c == '"' || c == '\\' || c == 0x7F || (c < 0x20 && c != '\t')) 620 p[j++] = '\\'; 621 p[j++] = c; 622 } 623 624 buffer_commit(b, j); 625 } 626 627 static void proxy_set_Forwarded(connection * const con, request_st * const r, const unsigned int flags) { 628 buffer *b = NULL, *efor = NULL, *eproto = NULL, *ehost = NULL; 629 int semicolon = 0; 630 631 if (proxy_check_extforward) { 632 efor = 633 http_header_env_get(r, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_FOR")); 634 eproto = 635 http_header_env_get(r, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_PROTO")); 636 ehost = 637 http_header_env_get(r, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_HOST")); 638 } 639 640 /* note: set "Forwarded" prior to updating X-Forwarded-For (below) */ 641 642 if (flags) 643 b = http_header_request_get(r, HTTP_HEADER_FORWARDED, CONST_STR_LEN("Forwarded")); 644 645 if (flags && NULL == b) { 646 const buffer *xff = 647 http_header_request_get(r, HTTP_HEADER_X_FORWARDED_FOR, CONST_STR_LEN("X-Forwarded-For")); 648 http_header_request_set(r, HTTP_HEADER_FORWARDED, 649 CONST_STR_LEN("Forwarded"), 650 CONST_STR_LEN("x")); /*(must not be blank for _get below)*/ 651 #ifdef __COVERITY__ 652 force_assert(NULL != b); /*(not NULL because created directly above)*/ 653 #endif 654 b = http_header_request_get(r, HTTP_HEADER_FORWARDED, CONST_STR_LEN("Forwarded")); 655 buffer_clear(b); 656 if (NULL != xff) { 657 /* use X-Forwarded-For contents to seed Forwarded */ 658 char *s = xff->ptr; 659 size_t used = buffer_string_length(xff); 660 for (size_t i=0, j, ipv6; i < used; ++i) { 661 while (s[i] == ' ' || s[i] == '\t' || s[i] == ',') ++i; 662 if (s[i] == '\0') break; 663 j = i; 664 do { 665 ++i; 666 } while (s[i]!=' ' && s[i]!='\t' && s[i]!=',' && s[i]!='\0'); 667 buffer_append_string_len(b, CONST_STR_LEN("for=")); 668 /* over-simplified test expecting only IPv4 or IPv6 addresses, 669 * (not expecting :port, so treat existence of colon as IPv6, 670 * and not expecting unix paths, especially not containing ':') 671 * quote all strings, backslash-escape since IPs not validated*/ 672 ipv6 = (NULL != memchr(s+j, ':', i-j)); /*(over-simplified) */ 673 buffer_append_string_len(b, CONST_STR_LEN("\"")); 674 if (ipv6) 675 buffer_append_string_len(b, CONST_STR_LEN("[")); 676 buffer_append_string_backslash_escaped(b, s+j, i-j); 677 if (ipv6) 678 buffer_append_string_len(b, CONST_STR_LEN("]")); 679 buffer_append_string_len(b, CONST_STR_LEN("\"")); 680 buffer_append_string_len(b, CONST_STR_LEN(", ")); 681 } 682 } 683 } else if (flags) { /*(NULL != b)*/ 684 buffer_append_string_len(b, CONST_STR_LEN(", ")); 685 } 686 687 if (flags & PROXY_FORWARDED_FOR) { 688 int family = sock_addr_get_family(&con->dst_addr); 689 buffer_append_string_len(b, CONST_STR_LEN("for=")); 690 if (NULL != efor) { 691 /* over-simplified test expecting only IPv4 or IPv6 addresses, 692 * (not expecting :port, so treat existence of colon as IPv6, 693 * and not expecting unix paths, especially not containing ':') 694 * quote all strings and backslash-escape since IPs not validated 695 * (should be IP from original con->dst_addr_buf, 696 * so trustable and without :port) */ 697 int ipv6 = (NULL != strchr(efor->ptr, ':')); 698 buffer_append_string_len(b, CONST_STR_LEN("\"")); 699 if (ipv6) buffer_append_string_len(b, CONST_STR_LEN("[")); 700 buffer_append_string_backslash_escaped( 701 b, CONST_BUF_LEN(efor)); 702 if (ipv6) buffer_append_string_len(b, CONST_STR_LEN("]")); 703 buffer_append_string_len(b, CONST_STR_LEN("\"")); 704 } else if (family == AF_INET) { 705 /*(Note: if :port is added, then must be quoted-string: 706 * e.g. for="...:port")*/ 707 buffer_append_string_buffer(b, con->dst_addr_buf); 708 } else if (family == AF_INET6) { 709 buffer_append_string_len(b, CONST_STR_LEN("\"[")); 710 buffer_append_string_buffer(b, con->dst_addr_buf); 711 buffer_append_string_len(b, CONST_STR_LEN("]\"")); 712 } else { 713 buffer_append_string_len(b, CONST_STR_LEN("\"")); 714 buffer_append_string_backslash_escaped( 715 b, CONST_BUF_LEN(con->dst_addr_buf)); 716 buffer_append_string_len(b, CONST_STR_LEN("\"")); 717 } 718 semicolon = 1; 719 } 720 721 if (flags & PROXY_FORWARDED_BY) { 722 int family = sock_addr_get_family(&con->srv_socket->addr); 723 /* Note: getsockname() and inet_ntop() are expensive operations. 724 * (recommendation: do not to enable by=... unless required) 725 * future: might use con->srv_socket->srv_token if addr is not 726 * INADDR_ANY or in6addr_any, but must omit optional :port 727 * from con->srv_socket->srv_token for consistency */ 728 729 if (semicolon) buffer_append_string_len(b, CONST_STR_LEN(";")); 730 buffer_append_string_len(b, CONST_STR_LEN("by=")); 731 buffer_append_string_len(b, CONST_STR_LEN("\"")); 732 #ifdef HAVE_SYS_UN_H 733 /* special-case: might need to encode unix domain socket path */ 734 if (family == AF_UNIX) { 735 buffer_append_string_backslash_escaped( 736 b, CONST_BUF_LEN(con->srv_socket->srv_token)); 737 } 738 else 739 #endif 740 { 741 sock_addr addr; 742 socklen_t addrlen = sizeof(addr); 743 if (0 == getsockname(con->fd,(struct sockaddr *)&addr, &addrlen)) { 744 sock_addr_stringify_append_buffer(b, &addr); 745 } 746 } 747 buffer_append_string_len(b, CONST_STR_LEN("\"")); 748 semicolon = 1; 749 } 750 751 if (flags & PROXY_FORWARDED_PROTO) { 752 /* expecting "http" or "https" 753 * (not checking if quoted-string and encoding needed) */ 754 if (semicolon) buffer_append_string_len(b, CONST_STR_LEN(";")); 755 buffer_append_string_len(b, CONST_STR_LEN("proto=")); 756 if (NULL != eproto) { 757 buffer_append_string_buffer(b, eproto); 758 } else if (con->srv_socket->is_ssl) { 759 buffer_append_string_len(b, CONST_STR_LEN("https")); 760 } else { 761 buffer_append_string_len(b, CONST_STR_LEN("http")); 762 } 763 semicolon = 1; 764 } 765 766 if (flags & PROXY_FORWARDED_HOST) { 767 if (NULL != ehost) { 768 if (semicolon) 769 buffer_append_string_len(b, CONST_STR_LEN(";")); 770 buffer_append_string_len(b, CONST_STR_LEN("host=\"")); 771 buffer_append_string_backslash_escaped( 772 b, CONST_BUF_LEN(ehost)); 773 buffer_append_string_len(b, CONST_STR_LEN("\"")); 774 semicolon = 1; 775 } else if (!buffer_string_is_empty(r->http_host)) { 776 if (semicolon) 777 buffer_append_string_len(b, CONST_STR_LEN(";")); 778 buffer_append_string_len(b, CONST_STR_LEN("host=\"")); 779 buffer_append_string_backslash_escaped( 780 b, CONST_BUF_LEN(r->http_host)); 781 buffer_append_string_len(b, CONST_STR_LEN("\"")); 782 semicolon = 1; 783 } 784 } 785 786 if (flags & PROXY_FORWARDED_REMOTE_USER) { 787 const buffer *remote_user = 788 http_header_env_get(r, CONST_STR_LEN("REMOTE_USER")); 789 if (NULL != remote_user) { 790 if (semicolon) 791 buffer_append_string_len(b, CONST_STR_LEN(";")); 792 buffer_append_string_len(b, CONST_STR_LEN("remote_user=\"")); 793 buffer_append_string_backslash_escaped( 794 b, CONST_BUF_LEN(remote_user)); 795 buffer_append_string_len(b, CONST_STR_LEN("\"")); 796 /*semicolon = 1;*/ 797 } 798 } 799 800 /* legacy X-* headers, including X-Forwarded-For */ 801 802 b = (NULL != efor) ? efor : con->dst_addr_buf; 803 http_header_request_set(r, HTTP_HEADER_X_FORWARDED_FOR, 804 CONST_STR_LEN("X-Forwarded-For"), 805 CONST_BUF_LEN(b)); 806 807 b = (NULL != ehost) ? ehost : r->http_host; 808 if (!buffer_string_is_empty(b)) { 809 http_header_request_set(r, HTTP_HEADER_OTHER, 810 CONST_STR_LEN("X-Host"), 811 CONST_BUF_LEN(b)); 812 http_header_request_set(r, HTTP_HEADER_OTHER, 813 CONST_STR_LEN("X-Forwarded-Host"), 814 CONST_BUF_LEN(b)); 815 } 816 817 b = (NULL != eproto) ? eproto : &r->uri.scheme; 818 http_header_request_set(r, HTTP_HEADER_X_FORWARDED_PROTO, 819 CONST_STR_LEN("X-Forwarded-Proto"), 820 CONST_BUF_LEN(b)); 821 } 822 823 824 static handler_t proxy_stdin_append(gw_handler_ctx *hctx) { 825 /*handler_ctx *hctx = (handler_ctx *)gwhctx;*/ 826 chunkqueue * const req_cq = &hctx->r->reqbody_queue; 827 const off_t req_cqlen = chunkqueue_length(req_cq); 828 if (req_cqlen) { 829 /* XXX: future: use http_chunk_len_append() */ 830 buffer * const tb = hctx->r->tmp_buf; 831 buffer_clear(tb); 832 buffer_append_uint_hex_lc(tb, (uintmax_t)req_cqlen); 833 buffer_append_string_len(tb, CONST_STR_LEN("\r\n")); 834 835 const off_t len = (off_t)buffer_string_length(tb) 836 + 2 /*(+2 end chunk "\r\n")*/ 837 + req_cqlen; 838 if (-1 != hctx->wb_reqlen) 839 hctx->wb_reqlen += (hctx->wb_reqlen >= 0) ? len : -len; 840 841 (chunkqueue_is_empty(&hctx->wb) || hctx->wb.first->type == MEM_CHUNK) 842 /* else FILE_CHUNK for temp file */ 843 ? chunkqueue_append_mem(&hctx->wb, CONST_BUF_LEN(tb)) 844 : chunkqueue_append_mem_min(&hctx->wb, CONST_BUF_LEN(tb)); 845 chunkqueue_steal(&hctx->wb, req_cq, req_cqlen); 846 847 chunkqueue_append_mem_min(&hctx->wb, CONST_STR_LEN("\r\n")); 848 } 849 850 if (hctx->wb.bytes_in == hctx->wb_reqlen) {/*hctx->r->reqbody_length >= 0*/ 851 /* terminate STDIN */ 852 chunkqueue_append_mem(&hctx->wb, CONST_STR_LEN("0\r\n\r\n")); 853 hctx->wb_reqlen += (int)sizeof("0\r\n\r\n"); 854 } 855 856 return HANDLER_GO_ON; 857 } 858 859 860 static handler_t proxy_create_env(gw_handler_ctx *gwhctx) { 861 handler_ctx *hctx = (handler_ctx *)gwhctx; 862 request_st * const r = hctx->gw.r; 863 const int remap_headers = (NULL != hctx->conf.header.urlpaths 864 || NULL != hctx->conf.header.hosts_request); 865 size_t rsz = (size_t)(r->read_queue.bytes_out - hctx->gw.wb.bytes_in); 866 if (rsz >= 65536) rsz = r->rqst_header_len; 867 buffer * const b = chunkqueue_prepend_buffer_open_sz(&hctx->gw.wb, rsz); 868 869 /* build header */ 870 871 /* request line */ 872 http_method_append(b, r->http_method); 873 buffer_append_string_len(b, CONST_STR_LEN(" ")); 874 buffer_append_string_buffer(b, &r->target); 875 if (remap_headers) 876 http_header_remap_uri(b, buffer_string_length(b) - buffer_string_length(&r->target), &hctx->conf.header, 1); 877 878 if (!proxy_force_http10) 879 buffer_append_string_len(b, CONST_STR_LEN(" HTTP/1.1\r\n")); 880 else 881 buffer_append_string_len(b, CONST_STR_LEN(" HTTP/1.0\r\n")); 882 883 if (hctx->conf.replace_http_host && !buffer_string_is_empty(hctx->gw.host->id)) { 884 if (hctx->gw.conf.debug > 1) { 885 log_error(r->conf.errh, __FILE__, __LINE__, 886 "proxy - using \"%s\" as HTTP Host", hctx->gw.host->id->ptr); 887 } 888 buffer_append_string_len(b, CONST_STR_LEN("Host: ")); 889 buffer_append_string_buffer(b, hctx->gw.host->id); 890 buffer_append_string_len(b, CONST_STR_LEN("\r\n")); 891 } else if (!buffer_string_is_empty(r->http_host)) { 892 buffer_append_string_len(b, CONST_STR_LEN("Host: ")); 893 buffer_append_string_buffer(b, r->http_host); 894 if (remap_headers) { 895 size_t alen = buffer_string_length(r->http_host); 896 http_header_remap_host(b, buffer_string_length(b) - alen, &hctx->conf.header, 1, alen); 897 } 898 buffer_append_string_len(b, CONST_STR_LEN("\r\n")); 899 } 900 901 /* "Forwarded" and legacy X- headers */ 902 proxy_set_Forwarded(r->con, r, hctx->conf.forwarded); 903 904 if (r->reqbody_length > 0 905 || (0 == r->reqbody_length 906 && !http_method_get_or_head(r->http_method))) { 907 /* set Content-Length if client sent Transfer-Encoding: chunked 908 * and not streaming to backend (request body has been fully received) */ 909 const buffer *vb = http_header_request_get(r, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length")); 910 if (NULL == vb) { 911 char buf[LI_ITOSTRING_LENGTH]; 912 http_header_request_set(r, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length"), 913 buf, li_itostrn(buf, sizeof(buf), r->reqbody_length)); 914 } 915 } 916 else if (!proxy_force_http10 917 && -1 == r->reqbody_length 918 && (r->conf.stream_request_body 919 & (FDEVENT_STREAM_REQUEST | FDEVENT_STREAM_REQUEST_BUFMIN))) { 920 hctx->gw.stdin_append = proxy_stdin_append; /* stream chunked body */ 921 buffer_append_string_len(b, CONST_STR_LEN("Transfer-Encoding: chunked\r\n")); 922 } 923 924 /* request header */ 925 const buffer *connhdr = NULL; 926 buffer *te = NULL; 927 buffer *upgrade = NULL; 928 for (size_t i = 0, used = r->rqst_headers.used; i < used; ++i) { 929 data_string *ds = (data_string *)r->rqst_headers.data[i]; 930 const size_t klen = buffer_string_length(&ds->key); 931 size_t vlen; 932 switch (klen) { 933 default: 934 break; 935 case 2: 936 if (buffer_is_equal_caseless_string(&ds->key, CONST_STR_LEN("TE"))) { 937 if (proxy_force_http10 || r->http_version == HTTP_VERSION_1_0) continue; 938 /* ignore if not exactly "trailers" */ 939 if (!buffer_eq_icase_slen(&ds->value, CONST_STR_LEN("trailers"))) continue; 940 te = &ds->value; 941 } 942 break; 943 case 4: 944 if (buffer_is_equal_caseless_string(&ds->key, CONST_STR_LEN("Host"))) continue; /*(handled further above)*/ 945 break; 946 case 7: 947 if (buffer_is_equal_caseless_string(&ds->key, CONST_STR_LEN("Upgrade"))) { 948 if (proxy_force_http10 || r->http_version == HTTP_VERSION_1_0) continue; 949 if (!hctx->conf.header.upgrade) continue; 950 upgrade = &ds->value; 951 } 952 break; 953 case 10: 954 if (buffer_is_equal_caseless_string(&ds->key, CONST_STR_LEN("Connection"))) { connhdr = &ds->value; continue; } 955 if (buffer_is_equal_caseless_string(&ds->key, CONST_STR_LEN("Set-Cookie"))) continue; /*(response header only; avoid accidental reflection)*/ 956 break; 957 case 16: 958 if (buffer_is_equal_caseless_string(&ds->key, CONST_STR_LEN("Proxy-Connection"))) continue; 959 break; 960 case 5: 961 /* Do not emit HTTP_PROXY in environment. 962 * Some executables use HTTP_PROXY to configure 963 * outgoing proxy. See also https://httpoxy.org/ */ 964 if (buffer_is_equal_caseless_string(&ds->key, CONST_STR_LEN("Proxy"))) continue; 965 break; 966 case 6: 967 /* Do not forward Expect: 100-continue 968 * since we do not handle "HTTP/1.1 100 Continue" response */ 969 if (buffer_is_equal_caseless_string(&ds->key, CONST_STR_LEN("Expect"))) continue; 970 break; 971 case 0: 972 continue; 973 } 974 975 vlen = buffer_string_length(&ds->value); 976 if (0 == vlen) continue; 977 978 if (buffer_string_space(b) < klen + vlen + 4) { 979 size_t extend = b->size * 2 - buffer_string_length(b); 980 extend = extend > klen + vlen + 4 ? extend : klen + vlen + 4 + 4095; 981 buffer_string_prepare_append(b, extend); 982 } 983 984 buffer_append_string_len(b, ds->key.ptr, klen); 985 buffer_append_string_len(b, CONST_STR_LEN(": ")); 986 buffer_append_string_len(b, ds->value.ptr, vlen); 987 buffer_append_string_len(b, CONST_STR_LEN("\r\n")); 988 989 if (!remap_headers) continue; 990 991 /* check for hdrs for which to remap URIs in-place after append to b */ 992 993 switch (klen) { 994 default: 995 continue; 996 #if 0 /* "URI" is HTTP response header (non-standard; historical in Apache) */ 997 case 3: 998 if (buffer_is_equal_caseless_string(&ds->key, CONST_STR_LEN("URI"))) break; 999 continue; 1000 #endif 1001 #if 0 /* "Location" is HTTP response header */ 1002 case 8: 1003 if (buffer_is_equal_caseless_string(&ds->key, CONST_STR_LEN("Location"))) break; 1004 continue; 1005 #endif 1006 case 11: /* "Destination" is WebDAV request header */ 1007 if (buffer_is_equal_caseless_string(&ds->key, CONST_STR_LEN("Destination"))) break; 1008 continue; 1009 case 16: /* "Content-Location" may be HTTP request or response header */ 1010 if (buffer_is_equal_caseless_string(&ds->key, CONST_STR_LEN("Content-Location"))) break; 1011 continue; 1012 } 1013 1014 http_header_remap_uri(b, buffer_string_length(b) - vlen - 2, &hctx->conf.header, 1); 1015 } 1016 1017 if (connhdr && !proxy_force_http10 && r->http_version >= HTTP_VERSION_1_1 1018 && !buffer_eq_icase_slen(connhdr, CONST_STR_LEN("close"))) { 1019 /* mod_proxy always sends Connection: close to backend */ 1020 buffer_append_string_len(b, CONST_STR_LEN("Connection: close")); 1021 /* (future: might be pedantic and also check Connection header for each 1022 * token using http_header_str_contains_token() */ 1023 if (!buffer_string_is_empty(te)) 1024 buffer_append_string_len(b, CONST_STR_LEN(", te")); 1025 if (!buffer_string_is_empty(upgrade)) 1026 buffer_append_string_len(b, CONST_STR_LEN(", upgrade")); 1027 buffer_append_string_len(b, CONST_STR_LEN("\r\n\r\n")); 1028 } 1029 else /* mod_proxy always sends Connection: close to backend */ 1030 buffer_append_string_len(b, CONST_STR_LEN("Connection: close\r\n\r\n")); 1031 1032 hctx->gw.wb_reqlen = buffer_string_length(b); 1033 chunkqueue_prepend_buffer_commit(&hctx->gw.wb); 1034 1035 if (r->reqbody_length) { 1036 chunkqueue_append_chunkqueue(&hctx->gw.wb, &r->reqbody_queue); 1037 if (r->reqbody_length > 0) 1038 hctx->gw.wb_reqlen += r->reqbody_length; /* total req size */ 1039 else /* as-yet-unknown total request size (Transfer-Encoding: chunked)*/ 1040 hctx->gw.wb_reqlen = -hctx->gw.wb_reqlen; 1041 } 1042 1043 status_counter_inc(CONST_STR_LEN("proxy.requests")); 1044 return HANDLER_GO_ON; 1045 } 1046 1047 1048 static handler_t proxy_create_env_connect(gw_handler_ctx *gwhctx) { 1049 handler_ctx *hctx = (handler_ctx *)gwhctx; 1050 request_st * const r = hctx->gw.r; 1051 r->http_status = 200; /* OK */ 1052 r->resp_body_started = 1; 1053 gw_set_transparent(&hctx->gw); 1054 http_response_upgrade_read_body_unknown(r); 1055 1056 status_counter_inc(CONST_STR_LEN("proxy.requests")); 1057 return HANDLER_GO_ON; 1058 } 1059 1060 1061 static handler_t proxy_response_headers(request_st * const r, struct http_response_opts_t *opts) { 1062 /* response headers just completed */ 1063 handler_ctx *hctx = (handler_ctx *)opts->pdata; 1064 1065 if (light_btst(r->resp_htags, HTTP_HEADER_UPGRADE)) { 1066 if (hctx->conf.header.upgrade && r->http_status == 101) { 1067 /* 101 Switching Protocols; transition to transparent proxy */ 1068 gw_set_transparent(&hctx->gw); 1069 http_response_upgrade_read_body_unknown(r); 1070 } 1071 else { 1072 light_bclr(r->resp_htags, HTTP_HEADER_UPGRADE); 1073 #if 0 1074 /* preserve prior questionable behavior; likely broken behavior 1075 * anyway if backend thinks connection is being upgraded but client 1076 * does not receive Connection: upgrade */ 1077 http_header_response_unset(r, HTTP_HEADER_UPGRADE, 1078 CONST_STR_LEN("Upgrade")) 1079 #endif 1080 } 1081 } 1082 1083 /* rewrite paths, if needed */ 1084 1085 if (NULL == hctx->conf.header.urlpaths 1086 && NULL == hctx->conf.header.hosts_response) 1087 return HANDLER_GO_ON; 1088 1089 if (light_btst(r->resp_htags, HTTP_HEADER_LOCATION)) { 1090 buffer *vb = http_header_response_get(r, HTTP_HEADER_LOCATION, CONST_STR_LEN("Location")); 1091 if (vb) http_header_remap_uri(vb, 0, &hctx->conf.header, 0); 1092 } 1093 if (light_btst(r->resp_htags, HTTP_HEADER_CONTENT_LOCATION)) { 1094 buffer *vb = http_header_response_get(r, HTTP_HEADER_CONTENT_LOCATION, CONST_STR_LEN("Content-Location")); 1095 if (vb) http_header_remap_uri(vb, 0, &hctx->conf.header, 0); 1096 } 1097 if (light_btst(r->resp_htags, HTTP_HEADER_SET_COOKIE)) { 1098 buffer *vb = http_header_response_get(r, HTTP_HEADER_SET_COOKIE, CONST_STR_LEN("Set-Cookie")); 1099 if (vb) http_header_remap_setcookie(vb, 0, &hctx->conf.header); 1100 } 1101 1102 return HANDLER_GO_ON; 1103 } 1104 1105 static handler_t mod_proxy_check_extension(request_st * const r, void *p_d) { 1106 plugin_data *p = p_d; 1107 handler_t rc; 1108 1109 if (NULL != r->handler_module) return HANDLER_GO_ON; 1110 1111 mod_proxy_patch_config(r, p); 1112 if (NULL == p->conf.gw.exts) return HANDLER_GO_ON; 1113 1114 rc = gw_check_extension(r, (gw_plugin_data *)p, 1, sizeof(handler_ctx)); 1115 if (HANDLER_GO_ON != rc) return rc; 1116 1117 if (r->handler_module == p->self) { 1118 handler_ctx *hctx = r->plugin_ctx[p->id]; 1119 hctx->gw.create_env = proxy_create_env; 1120 hctx->gw.response = chunk_buffer_acquire(); 1121 hctx->gw.opts.backend = BACKEND_PROXY; 1122 hctx->gw.opts.pdata = hctx; 1123 hctx->gw.opts.headers = proxy_response_headers; 1124 1125 hctx->conf = p->conf; /*(copies struct)*/ 1126 hctx->conf.header.http_host = r->http_host; 1127 hctx->conf.header.upgrade &= (r->http_version == HTTP_VERSION_1_1); 1128 /* mod_proxy currently sends all backend requests as http. 1129 * https-remap is a flag since it might not be needed if backend 1130 * honors Forwarded or X-Forwarded-Proto headers, e.g. by using 1131 * lighttpd mod_extforward or similar functionality in backend*/ 1132 if (hctx->conf.header.https_remap) { 1133 hctx->conf.header.https_remap = 1134 buffer_is_equal_string(&r->uri.scheme, CONST_STR_LEN("https")); 1135 } 1136 1137 if (r->http_method == HTTP_METHOD_CONNECT) { 1138 /*(note: not requiring HTTP/1.1 due to too many non-compliant 1139 * clients such as 'openssl s_client')*/ 1140 if (hctx->conf.header.connect_method) { 1141 hctx->gw.create_env = proxy_create_env_connect; 1142 } 1143 else { 1144 r->http_status = 405; /* Method Not Allowed */ 1145 r->handler_module = NULL; 1146 return HANDLER_FINISHED; 1147 } 1148 } 1149 } 1150 1151 return HANDLER_GO_ON; 1152 } 1153 1154 1155 int mod_proxy_plugin_init(plugin *p); 1156 int mod_proxy_plugin_init(plugin *p) { 1157 p->version = LIGHTTPD_VERSION_ID; 1158 p->name = "proxy"; 1159 1160 p->init = mod_proxy_init; 1161 p->cleanup = mod_proxy_free; 1162 p->set_defaults = mod_proxy_set_defaults; 1163 p->handle_request_reset = gw_handle_request_reset; 1164 p->handle_uri_clean = mod_proxy_check_extension; 1165 p->handle_subrequest = gw_handle_subrequest; 1166 p->handle_trigger = gw_handle_trigger; 1167 p->handle_waitpid = gw_handle_waitpid_cb; 1168 1169 return 0; 1170 } 1171