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