1 #include "first.h" 2 3 #include "base.h" 4 #include "log.h" 5 #include "buffer.h" 6 #include "http_header.h" 7 #include "request.h" 8 #include "sock_addr.h" 9 10 #include "plugin.h" 11 12 #include <limits.h> 13 #include <stdlib.h> 14 #include <string.h> 15 #include <errno.h> 16 17 #include "sys-socket.h" 18 19 /** 20 * mod_extforward.c for lighttpd, by comman.kang <at> gmail <dot> com 21 * extended, modified by Lionel Elie Mamane (LEM), lionel <at> mamane <dot> lu 22 * support chained proxies by [email protected], #1528 23 * 24 * 25 * Mostly rewritten 26 * Portions: 27 * Copyright(c) 2017 Glenn Strauss gstrauss()gluelogic.com All rights reserved 28 * License: BSD 3-clause (same as lighttpd) 29 * 30 * Config example: 31 * 32 * Trust proxy 10.0.0.232 and 10.0.0.232 33 * extforward.forwarder = ( "10.0.0.232" => "trust", 34 * "10.0.0.233" => "trust" ) 35 * 36 * Trust all proxies (NOT RECOMMENDED!) 37 * extforward.forwarder = ( "all" => "trust") 38 * 39 * Note that "all" has precedence over specific entries, 40 * so "all except" setups will not work. 41 * 42 * In case you have chained proxies, you can add all their IP's to the 43 * config. However "all" has effect only on connecting IP, as the 44 * X-Forwarded-For header can not be trusted. 45 * 46 * Note: The effect of this module is variable on $HTTP["remotip"] directives and 47 * other module's remote ip dependent actions. 48 * Things done by modules before we change the remoteip or after we reset it will match on the proxy's IP. 49 * Things done in between these two moments will match on the real client's IP. 50 * The moment things are done by a module depends on in which hook it does things and within the same hook 51 * on whether they are before/after us in the module loading order 52 * (order in the server.modules directive in the config file). 53 * 54 * Tested behaviours: 55 * 56 * mod_access: Will match on the real client. 57 * 58 * mod_accesslog: 59 * In order to see the "real" ip address in access log , 60 * you'll have to load mod_extforward after mod_accesslog. 61 * like this: 62 * 63 * server.modules = ( 64 * ..... 65 * mod_accesslog, 66 * mod_extforward 67 * ) 68 */ 69 70 71 typedef enum { 72 PROXY_FORWARDED_NONE = 0x00, 73 PROXY_FORWARDED_FOR = 0x01, 74 PROXY_FORWARDED_PROTO = 0x02, 75 PROXY_FORWARDED_HOST = 0x04, 76 PROXY_FORWARDED_BY = 0x08, 77 PROXY_FORWARDED_REMOTE_USER = 0x10 78 } proxy_forwarded_t; 79 80 struct sock_addr_mask { 81 sock_addr addr; 82 int bits; 83 }; 84 85 struct forwarder_cfg { 86 const array *forwarder; 87 int forward_all; 88 uint32_t addrs_used; 89 #if defined(__STDC_VERSION__) && __STDC_VERSION__-0 >= 199901L /* C99 */ 90 struct sock_addr_mask addrs[]; 91 #else 92 struct sock_addr_mask addrs[1]; 93 #endif 94 }; 95 96 typedef struct { 97 const array *forwarder; 98 int forward_all; 99 uint32_t forward_masks_used; 100 const struct sock_addr_mask *forward_masks; 101 const array *headers; 102 unsigned int opts; 103 char hap_PROXY; 104 char hap_PROXY_ssl_client_verify; 105 } plugin_config; 106 107 typedef struct { 108 PLUGIN_DATA; 109 plugin_config defaults; 110 plugin_config conf; 111 array *default_headers; 112 } plugin_data; 113 114 static plugin_data *mod_extforward_plugin_data_singleton; 115 static int extforward_check_proxy; 116 117 118 /* context , used for restore remote ip */ 119 120 typedef struct { 121 /* per-request state */ 122 sock_addr saved_remote_addr; 123 buffer *saved_remote_addr_buf; 124 125 /* hap-PROXY protocol prior to receiving first request */ 126 int(*saved_network_read)(connection *, chunkqueue *, off_t); 127 128 /* connection-level state applied to requests in handle_request_env */ 129 array *env; 130 int ssl_client_verify; 131 uint32_t request_count; 132 } handler_ctx; 133 134 135 static handler_ctx * handler_ctx_init(void) { 136 handler_ctx * hctx; 137 hctx = calloc(1, sizeof(*hctx)); 138 force_assert(hctx); 139 return hctx; 140 } 141 142 static void handler_ctx_free(handler_ctx *hctx) { 143 free(hctx); 144 } 145 146 INIT_FUNC(mod_extforward_init) { 147 return calloc(1, sizeof(plugin_data)); 148 } 149 150 FREE_FUNC(mod_extforward_free) { 151 plugin_data * const p = p_d; 152 array_free(p->default_headers); 153 if (NULL == p->cvlist) return; 154 /* (init i to 0 if global context; to 1 to skip empty global context) */ 155 for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) { 156 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 157 for (; -1 != cpv->k_id; ++cpv) { 158 switch (cpv->k_id) { 159 case 0: /* extforward.forwarder */ 160 if (cpv->vtype == T_CONFIG_LOCAL) free(cpv->v.v); 161 break; 162 default: 163 break; 164 } 165 } 166 } 167 } 168 169 static void mod_extforward_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) { 170 switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ 171 case 0: /* extforward.forwarder */ 172 if (cpv->vtype == T_CONFIG_LOCAL) { 173 const struct forwarder_cfg * const fwd = cpv->v.v; 174 pconf->forwarder = fwd->forwarder; 175 pconf->forward_all = fwd->forward_all; 176 pconf->forward_masks_used = fwd->addrs_used; 177 pconf->forward_masks = fwd->addrs; 178 } 179 break; 180 case 1: /* extforward.headers */ 181 pconf->headers = cpv->v.a; 182 break; 183 case 2: /* extforward.params */ 184 if (cpv->vtype == T_CONFIG_LOCAL) 185 pconf->opts = cpv->v.u; 186 break; 187 case 3: /* extforward.hap-PROXY */ 188 pconf->hap_PROXY = (char)cpv->v.u; 189 break; 190 case 4: /* extforward.hap-PROXY-ssl-client-verify */ 191 pconf->hap_PROXY_ssl_client_verify = (char)cpv->v.u; 192 break; 193 default:/* should not happen */ 194 return; 195 } 196 } 197 198 static void mod_extforward_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) { 199 do { 200 mod_extforward_merge_config_cpv(pconf, cpv); 201 } while ((++cpv)->k_id != -1); 202 } 203 204 static void mod_extforward_patch_config(request_st * const r, plugin_data * const p) { 205 memcpy(&p->conf, &p->defaults, sizeof(plugin_config)); 206 for (int i = 1, used = p->nconfig; i < used; ++i) { 207 if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id)) 208 mod_extforward_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]); 209 } 210 } 211 212 static void * mod_extforward_parse_forwarder(server *srv, const array *forwarder) { 213 const data_string * const allds = (const data_string *) 214 array_get_element_klen(forwarder, CONST_STR_LEN("all")); 215 const int forward_all = (NULL == allds) 216 ? 0 217 : buffer_eq_icase_slen(&allds->value, CONST_STR_LEN("trust")) ? 1 : -1; 218 uint32_t nmasks = 0; 219 for (uint32_t j = 0; j < forwarder->used; ++j) { 220 data_string * const ds = (data_string *)forwarder->data[j]; 221 char * const nm_slash = strchr(ds->key.ptr, '/'); 222 if (NULL != nm_slash) ++nmasks; 223 if (!buffer_eq_icase_slen(&ds->value, CONST_STR_LEN("trust"))) { 224 if (!buffer_eq_icase_slen(&ds->value, CONST_STR_LEN("untrusted"))) 225 log_error(srv->errh, __FILE__, __LINE__, 226 "ERROR: expect \"trust\", not \"%s\" => \"%s\"; " 227 "treating as untrusted", ds->key.ptr, ds->value.ptr); 228 if (NULL != nm_slash) { 229 /* future: consider adding member next to bits in sock_addr_mask 230 * with bool trusted/untrusted member */ 231 --nmasks; 232 log_error(srv->errh, __FILE__, __LINE__, 233 "ERROR: untrusted CIDR masks are ignored (\"%s\" => \"%s\")", 234 ds->key.ptr, ds->value.ptr); 235 } 236 buffer_clear(&ds->value); /* empty is untrusted */ 237 continue; 238 } 239 } 240 241 struct forwarder_cfg * const fwd = 242 malloc(sizeof(struct forwarder_cfg)+sizeof(struct sock_addr_mask)*nmasks); 243 force_assert(fwd); 244 memset(fwd, 0, 245 sizeof(struct forwarder_cfg) + sizeof(struct sock_addr_mask)*nmasks); 246 fwd->forwarder = forwarder; 247 fwd->forward_all = forward_all; 248 fwd->addrs_used = 0; 249 for (uint32_t j = 0; j < forwarder->used; ++j) { 250 data_string * const ds = (data_string *)forwarder->data[j]; 251 char * const nm_slash = strchr(ds->key.ptr, '/'); 252 if (NULL == nm_slash) continue; 253 if (buffer_string_is_empty(&ds->value)) continue; /* ignored */ 254 255 char *err; 256 const int nm_bits = strtol(nm_slash + 1, &err, 10); 257 int rc; 258 if (*err || nm_bits <= 0 || !light_isdigit(nm_slash[1])) { 259 log_error(srv->errh, __FILE__, __LINE__, 260 "ERROR: invalid netmask: %s %s", ds->key.ptr, err); 261 free(fwd); 262 return NULL; 263 } 264 struct sock_addr_mask * const sm = fwd->addrs + fwd->addrs_used++; 265 sm->bits = nm_bits; 266 *nm_slash = '\0'; 267 rc = sock_addr_from_str_numeric(&sm->addr, ds->key.ptr, srv->errh); 268 *nm_slash = '/'; 269 if (1 != rc) { 270 free(fwd); 271 return NULL; 272 } 273 buffer_clear(&ds->value); 274 /* empty is untrusted, 275 * e.g. if subnet (incorrectly) appears in X-Forwarded-For */ 276 } 277 278 return fwd; 279 } 280 281 static unsigned int mod_extforward_parse_opts(server *srv, const array *opts_params) { 282 unsigned int opts = 0; 283 for (uint32_t j = 0, used = opts_params->used; j < used; ++j) { 284 proxy_forwarded_t param; 285 data_unset *du = opts_params->data[j]; 286 #if 0 /*("for" and "proto" historical behavior: always enabled)*/ 287 if (buffer_eq_slen(&du->key, CONST_STR_LEN("by"))) 288 param = PROXY_FORWARDED_BY; 289 else if (buffer_eq_slen(&du->key, CONST_STR_LEN("for"))) 290 param = PROXY_FORWARDED_FOR; 291 else 292 #endif 293 if (buffer_eq_slen(&du->key, CONST_STR_LEN("host"))) 294 param = PROXY_FORWARDED_HOST; 295 #if 0 296 else if (buffer_eq_slen(&du->key, CONST_STR_LEN("proto"))) 297 param = PROXY_FORWARDED_PROTO; 298 #endif 299 else if (buffer_eq_slen(&du->key, CONST_STR_LEN("remote_user"))) 300 param = PROXY_FORWARDED_REMOTE_USER; 301 else { 302 log_error(srv->errh, __FILE__, __LINE__, 303 "extforward.params keys must be one of: " 304 "host, remote_user, but not: %s", du->key.ptr); 305 return HANDLER_ERROR; 306 } 307 308 if (du->type == TYPE_STRING) { 309 data_string *ds = (data_string *)du; 310 if (buffer_eq_slen(&ds->value, CONST_STR_LEN("enable"))) { 311 opts |= param; 312 } 313 else if (!buffer_eq_slen(&ds->value, CONST_STR_LEN("disable"))) { 314 log_error(srv->errh, __FILE__, __LINE__, 315 "extforward.params values must be one of: " 316 "0, 1, enable, disable; error for key: %s", du->key.ptr); 317 return UINT_MAX; 318 } 319 } 320 else if (du->type == TYPE_INTEGER) { 321 data_integer *di = (data_integer *)du; 322 if (di->value) opts |= param; 323 } 324 else { 325 log_error(srv->errh, __FILE__, __LINE__, 326 "extforward.params values must be one of: " 327 "0, 1, enable, disable; error for key: %s", du->key.ptr); 328 return UINT_MAX; 329 } 330 } 331 return opts; 332 } 333 334 SETDEFAULTS_FUNC(mod_extforward_set_defaults) { 335 static const config_plugin_keys_t cpk[] = { 336 { CONST_STR_LEN("extforward.forwarder"), 337 T_CONFIG_ARRAY_KVSTRING, 338 T_CONFIG_SCOPE_CONNECTION } 339 ,{ CONST_STR_LEN("extforward.headers"), 340 T_CONFIG_ARRAY_VLIST, 341 T_CONFIG_SCOPE_CONNECTION } 342 ,{ CONST_STR_LEN("extforward.params"), 343 T_CONFIG_ARRAY_KVANY, 344 T_CONFIG_SCOPE_CONNECTION } 345 ,{ CONST_STR_LEN("extforward.hap-PROXY"), 346 T_CONFIG_BOOL, 347 T_CONFIG_SCOPE_CONNECTION } 348 ,{ CONST_STR_LEN("extforward.hap-PROXY-ssl-client-verify"), 349 T_CONFIG_BOOL, 350 T_CONFIG_SCOPE_CONNECTION } 351 ,{ NULL, 0, 352 T_CONFIG_UNSET, 353 T_CONFIG_SCOPE_UNSET } 354 }; 355 356 plugin_data * const p = p_d; 357 if (!config_plugin_values_init(srv, p, cpk, "mod_extforward")) 358 return HANDLER_ERROR; 359 360 int hap_PROXY = 0; 361 362 /* process and validate config directives 363 * (init i to 0 if global context; to 1 to skip empty global context) */ 364 for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { 365 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 366 for (; -1 != cpv->k_id; ++cpv) { 367 switch (cpv->k_id) { 368 case 0: /* extforward.forwarder */ 369 cpv->v.v = mod_extforward_parse_forwarder(srv, cpv->v.a); 370 if (NULL == cpv->v.v) { 371 log_error(srv->errh, __FILE__, __LINE__, 372 "unexpected value for %s", cpk[cpv->k_id].k); 373 return HANDLER_ERROR; 374 } 375 cpv->vtype = T_CONFIG_LOCAL; 376 break; 377 case 1: /* extforward.headers */ 378 if (cpv->v.a->used) { 379 array *a; 380 *(const array **)&a = cpv->v.a; 381 for (uint32_t j = 0; j < a->used; ++j) { 382 data_string * const ds = (data_string *)a->data[j]; 383 ds->ext = 384 http_header_hkey_get(CONST_BUF_LEN(&ds->value)); 385 } 386 } 387 break; 388 case 2: /* extforward.params */ 389 cpv->v.u = mod_extforward_parse_opts(srv, cpv->v.a); 390 if (UINT_MAX == cpv->v.u) 391 return HANDLER_ERROR; 392 break; 393 case 3: /* extforward.hap-PROXY */ 394 if (cpv->v.u) hap_PROXY = 1; 395 break; 396 case 4: /* extforward.hap-PROXY-ssl-client-verify */ 397 break; 398 default:/* should not happen */ 399 break; 400 } 401 } 402 } 403 404 mod_extforward_plugin_data_singleton = p; 405 p->defaults.opts = PROXY_FORWARDED_NONE; 406 407 /* initialize p->defaults from global config context */ 408 if (p->nconfig > 0 && p->cvlist->v.u2[1]) { 409 const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; 410 if (-1 != cpv->k_id) 411 mod_extforward_merge_config(&p->defaults, cpv); 412 } 413 414 /* default to "X-Forwarded-For" or "Forwarded-For" if extforward.headers 415 * is not specified or is empty (and not using hap_PROXY) */ 416 if (!p->defaults.hap_PROXY 417 && (NULL == p->defaults.headers || 0 == p->defaults.headers->used)) { 418 p->defaults.headers = p->default_headers = array_init(2); 419 array_insert_value(p->default_headers,CONST_STR_LEN("X-Forwarded-For")); 420 array_insert_value(p->default_headers,CONST_STR_LEN("Forwarded-For")); 421 } 422 423 /* attempt to warn if mod_extforward is not last module loaded to hook 424 * handle_connection_accept. (Nice to have, but remove this check if 425 * it reaches too far into internals and prevents other code changes.) 426 * While it would be nice to check handle_connection_accept plugin slot 427 * to make sure mod_extforward is last, that info is private to plugin.c 428 * so merely warn if mod_openssl is loaded after mod_extforward, though 429 * future modules which hook handle_connection_accept might be missed.*/ 430 if (hap_PROXY) { 431 uint32_t i; 432 for (i = 0; i < srv->srvconf.modules->used; ++i) { 433 data_string *ds = (data_string *)srv->srvconf.modules->data[i]; 434 if (buffer_eq_slen(&ds->value, CONST_STR_LEN("mod_extforward"))) 435 break; 436 } 437 for (; i < srv->srvconf.modules->used; ++i) { 438 data_string *ds = (data_string *)srv->srvconf.modules->data[i]; 439 if (buffer_eq_slen(&ds->value, CONST_STR_LEN("mod_openssl")) 440 || buffer_eq_slen(&ds->value, CONST_STR_LEN("mod_mbedtls")) 441 || buffer_eq_slen(&ds->value, CONST_STR_LEN("mod_nss")) 442 || buffer_eq_slen(&ds->value, CONST_STR_LEN("mod_gnutls"))) { 443 log_error(srv->errh, __FILE__, __LINE__, 444 "mod_extforward must be loaded after %s in " 445 "server.modules when extforward.hap-PROXY = \"enable\"", 446 ds->value.ptr); 447 break; 448 } 449 } 450 } 451 452 for (uint32_t i = 0; i < srv->srvconf.modules->used; ++i) { 453 data_string *ds = (data_string *)srv->srvconf.modules->data[i]; 454 if (buffer_is_equal_string(&ds->value, CONST_STR_LEN("mod_proxy"))) { 455 extforward_check_proxy = 1; 456 break; 457 } 458 } 459 460 return HANDLER_GO_ON; 461 } 462 463 464 /* 465 extract a forward array from the environment 466 */ 467 static array *extract_forward_array(const buffer *pbuffer) 468 { 469 array *result = array_init(8); 470 if (!buffer_string_is_empty(pbuffer)) { 471 const char *base, *curr; 472 /* state variable, 0 means not in string, 1 means in string */ 473 int in_str = 0; 474 for (base = pbuffer->ptr, curr = pbuffer->ptr; *curr; curr++) { 475 int hex_or_colon = (light_isxdigit(*curr) || *curr == ':'); 476 if (in_str) { 477 if (!hex_or_colon && *curr != '.') { 478 /* found an separator , insert value into result array */ 479 array_insert_value(result, base, curr - base); 480 /* change state to not in string */ 481 in_str = 0; 482 } 483 } else { 484 if (hex_or_colon) { 485 /* found leading char of an IP address, move base pointer and change state */ 486 base = curr; 487 in_str = 1; 488 } 489 } 490 } 491 /* if breaking out while in str, we got to the end of string, so add it */ 492 if (in_str) { 493 array_insert_value(result, base, curr - base); 494 } 495 } 496 return result; 497 } 498 499 /* 500 * check whether ip is trusted, return 1 for trusted , 0 for untrusted 501 */ 502 static int is_proxy_trusted(plugin_data *p, const char * const ip, size_t iplen) 503 { 504 const data_string *ds = 505 (const data_string *)array_get_element_klen(p->conf.forwarder, ip, iplen); 506 if (NULL != ds) return !buffer_string_is_empty(&ds->value); 507 508 if (p->conf.forward_masks_used) { 509 const struct sock_addr_mask * const addrs = p->conf.forward_masks; 510 const uint32_t aused = p->conf.forward_masks_used; 511 sock_addr addr; 512 /* C funcs inet_aton(), inet_pton() require '\0'-terminated IP str */ 513 char addrstr[64]; /*(larger than INET_ADDRSTRLEN and INET6_ADDRSTRLEN)*/ 514 if (0 == iplen || iplen >= sizeof(addrstr)) return 0; 515 memcpy(addrstr, ip, iplen); 516 addrstr[iplen] = '\0'; 517 518 if (1 != sock_addr_inet_pton(&addr, addrstr, AF_INET, 0) 519 && 1 != sock_addr_inet_pton(&addr, addrstr, AF_INET6, 0)) return 0; 520 521 for (uint32_t i = 0; i < aused; ++i) { 522 if (sock_addr_is_addr_eq_bits(&addr, &addrs[i].addr, addrs[i].bits)) 523 return 1; 524 } 525 } 526 527 return 0; 528 } 529 530 static int is_connection_trusted(connection * const con, plugin_data *p) 531 { 532 if (p->conf.forward_all) return (1 == p->conf.forward_all); 533 return is_proxy_trusted(p, CONST_BUF_LEN(con->dst_addr_buf)); 534 } 535 536 /* 537 * Return last address of proxy that is not trusted. 538 * Do not accept "all" keyword here. 539 */ 540 static const char *last_not_in_array(array *a, plugin_data *p) 541 { 542 int i; 543 544 for (i = a->used - 1; i >= 0; i--) { 545 data_string *ds = (data_string *)a->data[i]; 546 if (!is_proxy_trusted(p, CONST_BUF_LEN(&ds->value))) { 547 return ds->value.ptr; 548 } 549 } 550 return NULL; 551 } 552 553 static int mod_extforward_set_addr(request_st * const r, plugin_data *p, const char *addr) { 554 connection * const con = r->con; 555 sock_addr sock; 556 handler_ctx *hctx = con->plugin_ctx[p->id]; 557 558 /* Preserve changed addr for lifetime of h2 connection; upstream proxy 559 * should not reuse same h2 connection for requests from different clients*/ 560 if (hctx && NULL != hctx->saved_remote_addr_buf 561 && r->http_version > HTTP_VERSION_1_1) { /*(e.g. HTTP_VERSION_2)*/ 562 if (extforward_check_proxy) /* save old address */ 563 http_header_env_set(r, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_FOR"), 564 CONST_BUF_LEN(hctx->saved_remote_addr_buf)); 565 return 1; 566 } 567 568 if (r->conf.log_request_handling) { 569 log_error(r->conf.errh, __FILE__, __LINE__, "using address: %s", addr); 570 } 571 572 sock.plain.sa_family = AF_UNSPEC; 573 if (1 != sock_addr_from_str_numeric(&sock, addr, r->conf.errh)) return 0; 574 if (sock.plain.sa_family == AF_UNSPEC) return 0; 575 576 /* we found the remote address, modify current connection and save the old address */ 577 if (hctx) { 578 if (hctx->saved_remote_addr_buf) { 579 if (r->conf.log_request_handling) { 580 log_error(r->conf.errh, __FILE__, __LINE__, 581 "-- mod_extforward_uri_handler already patched this connection, resetting state"); 582 } 583 con->dst_addr = hctx->saved_remote_addr; 584 buffer_free(con->dst_addr_buf); 585 con->dst_addr_buf = hctx->saved_remote_addr_buf; 586 hctx->saved_remote_addr_buf = NULL; 587 } 588 } else { 589 con->plugin_ctx[p->id] = hctx = handler_ctx_init(); 590 } 591 /* save old address */ 592 if (extforward_check_proxy) { 593 http_header_env_set(r, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_FOR"), CONST_BUF_LEN(con->dst_addr_buf)); 594 } 595 hctx->request_count = con->request_count; 596 hctx->saved_remote_addr = con->dst_addr; 597 hctx->saved_remote_addr_buf = con->dst_addr_buf; 598 /* patch connection address */ 599 con->dst_addr = sock; 600 con->dst_addr_buf = buffer_init_string(addr); 601 602 if (r->conf.log_request_handling) { 603 log_error(r->conf.errh, __FILE__, __LINE__, 604 "patching con->dst_addr_buf for the accesslog: %s", addr); 605 } 606 607 /* Now, clean the conf_cond cache, because we may have changed the results of tests */ 608 config_cond_cache_reset_item(r, COMP_HTTP_REMOTE_IP); 609 610 return 1; 611 } 612 613 static void mod_extforward_set_proto(request_st * const r, const char * const proto, size_t protolen) { 614 if (0 != protolen && !buffer_is_equal_caseless_string(&r->uri.scheme, proto, protolen)) { 615 /* update scheme if X-Forwarded-Proto is set 616 * Limitations: 617 * - Only "http" or "https" are currently accepted since the request to lighttpd currently has to 618 * be HTTP/1.0 or HTTP/1.1 using http or https. If this is changed, then the scheme from this 619 * untrusted header must be checked to contain only alphanumeric characters, and to be a 620 * reasonable length, e.g. < 256 chars. 621 * - r->uri.scheme is not reset in mod_extforward_restore() but is currently not an issues since 622 * r->uri.scheme will be reset by next request. If a new module uses r->uri.scheme in the 623 * handle_request_done hook, then should evaluate if that module should use the forwarded value 624 * (probably) or the original value. 625 */ 626 if (extforward_check_proxy) { 627 http_header_env_set(r, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_PROTO"), CONST_BUF_LEN(&r->uri.scheme)); 628 } 629 if (buffer_eq_icase_ss(proto, protolen, CONST_STR_LEN("https"))) { 630 r->con->proto_default_port = 443; /* "https" */ 631 buffer_copy_string_len(&r->uri.scheme, CONST_STR_LEN("https")); 632 config_cond_cache_reset_item(r, COMP_HTTP_SCHEME); 633 } else if (buffer_eq_icase_ss(proto, protolen, CONST_STR_LEN("http"))) { 634 r->con->proto_default_port = 80; /* "http" */ 635 buffer_copy_string_len(&r->uri.scheme, CONST_STR_LEN("http")); 636 config_cond_cache_reset_item(r, COMP_HTTP_SCHEME); 637 } 638 } 639 } 640 641 static handler_t mod_extforward_X_Forwarded_For(request_st * const r, plugin_data * const p, const buffer * const x_forwarded_for) { 642 /* build forward_array from forwarded data_string */ 643 array *forward_array = extract_forward_array(x_forwarded_for); 644 const char *real_remote_addr = last_not_in_array(forward_array, p); 645 if (real_remote_addr != NULL) { /* parsed */ 646 /* get scheme if X-Forwarded-Proto is set 647 * Limitations: 648 * - X-Forwarded-Proto may or may not be set by proxies, even if X-Forwarded-For is set 649 * - X-Forwarded-Proto may be a comma-separated list if there are multiple proxies, 650 * but the historical behavior of the code below only honored it if there was exactly one value 651 * (not done: walking backwards in X-Forwarded-Proto the same num of steps 652 * as in X-Forwarded-For to find proto set by last trusted proxy) 653 */ 654 const buffer *x_forwarded_proto = http_header_request_get(r, HTTP_HEADER_X_FORWARDED_PROTO, CONST_STR_LEN("X-Forwarded-Proto")); 655 if (mod_extforward_set_addr(r, p, real_remote_addr) && NULL != x_forwarded_proto) { 656 mod_extforward_set_proto(r, CONST_BUF_LEN(x_forwarded_proto)); 657 } 658 } 659 array_free(forward_array); 660 return HANDLER_GO_ON; 661 } 662 663 static int find_end_quoted_string (const char * const s, int i) { 664 do { 665 ++i; 666 } while (s[i] != '"' && s[i] != '\0' && (s[i] != '\\' || s[++i] != '\0')); 667 return i; 668 } 669 670 static int find_next_semicolon_or_comma_or_eq (const char * const s, int i) { 671 for (; s[i] != '=' && s[i] != ';' && s[i] != ',' && s[i] != '\0'; ++i) { 672 if (s[i] == '"') { 673 i = find_end_quoted_string(s, i); 674 if (s[i] == '\0') return -1; 675 } 676 } 677 return i; 678 } 679 680 static int find_next_semicolon_or_comma (const char * const s, int i) { 681 for (; s[i] != ';' && s[i] != ',' && s[i] != '\0'; ++i) { 682 if (s[i] == '"') { 683 i = find_end_quoted_string(s, i); 684 if (s[i] == '\0') return -1; 685 } 686 } 687 return i; 688 } 689 690 static int buffer_backslash_unescape (buffer * const b) { 691 /* (future: might move to buffer.c) */ 692 size_t j = 0; 693 size_t len = buffer_string_length(b); 694 char *p = memchr(b->ptr, '\\', len); 695 696 if (NULL == p) return 1; /*(nothing to do)*/ 697 698 len -= (size_t)(p - b->ptr); 699 for (size_t i = 0; i < len; ++i) { 700 if (p[i] == '\\') { 701 if (++i == len) return 0; /*(invalid trailing backslash)*/ 702 } 703 p[j++] = p[i]; 704 } 705 buffer_string_set_length(b, (size_t)(p+j - b->ptr)); 706 return 1; 707 } 708 709 static handler_t mod_extforward_Forwarded (request_st * const r, plugin_data * const p, const buffer * const forwarded) { 710 /* HTTP list need not consist of param=value tokens, 711 * but this routine expect such for HTTP Forwarded header 712 * Since info in each set of params is only used if from 713 * admin-specified trusted proxy: 714 * - invalid param=value tokens are ignored and skipped 715 * - not checking "for" exists in each set of params 716 * - not checking for duplicated params in each set of params 717 * - not checking canonical form of addr (also might be obfuscated) 718 * - obfuscated tokens permitted in chain, though end of trust is expected 719 * to be non-obfuscated IP for mod_extforward to masquerade as remote IP 720 * future: since (potentially) trusted proxies begin at end of string, 721 * it might be better to parse from end of string rather than parsing from 722 * beginning. Doing so would also allow reducing arbitrary param limit 723 * to number of params permitted per proxy. 724 */ 725 char * const s = forwarded->ptr; 726 int i = 0, j = -1, v, vlen, k, klen; 727 int used = (int)buffer_string_length(forwarded); 728 int ofor = -1, oproto, ohost, oby, oremote_user; 729 int offsets[256];/*(~50 params is more than reasonably expected to handle)*/ 730 while (i < used) { 731 while (s[i] == ' ' || s[i] == '\t') ++i; 732 if (s[i] == ';') { ++i; continue; } 733 if (s[i] == ',') { 734 if (j >= (int)(sizeof(offsets)/sizeof(int))) break; 735 offsets[++j] = -1; /*("offset" separating params from next proxy)*/ 736 ++i; 737 continue; 738 } 739 if (s[i] == '\0') break; 740 741 k = i; 742 i = find_next_semicolon_or_comma_or_eq(s, i); 743 if (i < 0) { 744 /*(reject IP spoofing if attacker sets improper quoted-string)*/ 745 log_error(r->conf.errh, __FILE__, __LINE__, 746 "invalid quoted-string in Forwarded header"); 747 r->http_status = 400; /* Bad Request */ 748 r->handler_module = NULL; 749 return HANDLER_FINISHED; 750 } 751 if (s[i] != '=') continue; 752 klen = i - k; 753 v = ++i; 754 i = find_next_semicolon_or_comma(s, i); 755 if (i < 0) { 756 /*(reject IP spoofing if attacker sets improper quoted-string)*/ 757 log_error(r->conf.errh, __FILE__, __LINE__, 758 "invalid quoted-string in Forwarded header"); 759 r->http_status = 400; /* Bad Request */ 760 r->handler_module = NULL; 761 return HANDLER_FINISHED; 762 } 763 vlen = i - v; /* might be 0 */ 764 765 /* have k, klen, v, vlen 766 * (might contain quoted string) (contents not validated or decoded) 767 * (might be repeated k) 768 */ 769 if (0 == klen) continue; /* invalid k */ 770 if (j >= (int)(sizeof(offsets)/sizeof(int))-4) break; 771 offsets[j+1] = k; 772 offsets[j+2] = klen; 773 offsets[j+3] = v; 774 offsets[j+4] = vlen; 775 j += 4; 776 } 777 778 if (j >= (int)(sizeof(offsets)/sizeof(int))-4) { 779 /* error processing Forwarded; too many params; fail closed */ 780 log_error(r->conf.errh, __FILE__, __LINE__, 781 "Too many params in Forwarded header"); 782 r->http_status = 400; /* Bad Request */ 783 r->handler_module = NULL; 784 return HANDLER_FINISHED; 785 } 786 787 if (-1 == j) return HANDLER_GO_ON; /* make no changes */ 788 used = j+1; 789 offsets[used] = -1; /* mark end of last set of params */ 790 791 while (j >= 4) { /*(param=value pairs)*/ 792 if (-1 == offsets[j]) { --j; continue; } 793 do { 794 j -= 3; /*(k, klen, v, vlen come in sets of 4)*/ 795 } while ((3 != offsets[j+1] /* 3 == sizeof("for")-1 */ 796 || !buffer_eq_icase_ssn(s+offsets[j], "for", 3)) 797 && 0 != j-- && -1 != offsets[j]); 798 if (j < 0) break; 799 if (-1 == offsets[j]) { --j; continue; } 800 801 /* remove trailing spaces/tabs and double-quotes from string 802 * (note: not unescaping backslash escapes in quoted string) */ 803 v = offsets[j+2]; 804 vlen = v + offsets[j+3]; 805 while (vlen > v && (s[vlen-1] == ' ' || s[vlen-1] == '\t')) --vlen; 806 if (vlen > v+1 && s[v] == '"' && s[vlen-1] == '"') { 807 offsets[j+2] = ++v; 808 --vlen; 809 if (s[v] == '[') { 810 /* remove "[]" surrounding IPv6, as well as (optional) port 811 * (assumes properly formatted IPv6 addr from trusted proxy) */ 812 ++v; 813 do { --vlen; } while (vlen > v && s[vlen] != ']'); 814 if (v == vlen) { 815 log_error(r->conf.errh, __FILE__, __LINE__, 816 "Invalid IPv6 addr in Forwarded header"); 817 r->http_status = 400; /* Bad Request */ 818 r->handler_module = NULL; 819 return HANDLER_FINISHED; 820 } 821 } 822 else if (s[v] != '_' && s[v] != '/' && s[v] != 'u') { 823 /* remove (optional) port from non-obfuscated IPv4 */ 824 for (klen=vlen, vlen=v; vlen < klen && s[vlen] != ':'; ++vlen) ; 825 } 826 offsets[j+2] = v; 827 } 828 offsets[j+3] = vlen - v; 829 830 /* obfuscated ipstr and obfuscated port are also accepted here, as 831 * is path to unix domain socket, but note that backslash escapes 832 * in quoted-string were not unescaped above. Also, if obfuscated 833 * identifiers are rotated by proxies as recommended by RFC, then 834 * maintaining list of trusted identifiers is non-trivial and is not 835 * attempted by this module. */ 836 837 if (v != vlen) { 838 int trusted = is_proxy_trusted(p, s+v, vlen-v); 839 840 if (s[v] != '_' && s[v] != '/' 841 && (7 != (vlen - v) || 0 != memcmp(s+v, "unknown", 7))) { 842 ofor = j; /* save most recent non-obfuscated ipstr */ 843 } 844 845 if (!trusted) break; 846 } 847 848 do { --j; } while (j > 0 && -1 != offsets[j]); 849 if (j <= 0) break; 850 --j; 851 } 852 853 if (-1 != ofor) { 854 /* C funcs getaddrinfo(), inet_addr() require '\0'-terminated IP str */ 855 char *ipend = s+offsets[ofor+2]+offsets[ofor+3]; 856 char c = *ipend; 857 int rc; 858 *ipend = '\0'; 859 rc = mod_extforward_set_addr(r, p, s+offsets[ofor+2]); 860 *ipend = c; 861 if (!rc) return HANDLER_GO_ON; /* invalid addr; make no changes */ 862 } 863 else { 864 return HANDLER_GO_ON; /* make no changes */ 865 } 866 867 /* parse out params associated with for=<ip> addr set above */ 868 oproto = ohost = oby = oremote_user = -1; 869 UNUSED(oby); 870 j = ofor; 871 if (j > 0) { do { --j; } while (j > 0 && -1 != offsets[j]); } 872 if (-1 == offsets[j]) ++j; 873 if (j == ofor) j += 4; 874 for (; -1 != offsets[j]; j+=4) { /*(k, klen, v, vlen come in sets of 4)*/ 875 switch (offsets[j+1]) { 876 #if 0 877 case 2: 878 if (buffer_eq_icase_ssn(s+offsets[j], "by", 2)) 879 oby = j; 880 break; 881 #endif 882 #if 0 883 /*(already handled above to find IP prior to earliest trusted proxy)*/ 884 case 3: 885 if (buffer_eq_icase_ssn(s+offsets[j], "for", 3)) 886 ofor = j; 887 break; 888 #endif 889 case 4: 890 if (buffer_eq_icase_ssn(s+offsets[j], "host", 4)) 891 ohost = j; 892 break; 893 case 5: 894 if (buffer_eq_icase_ssn(s+offsets[j], "proto", 5)) 895 oproto = j; 896 break; 897 case 11: 898 if (buffer_eq_icase_ssn(s+offsets[j], "remote_user", 11)) 899 oremote_user = j; 900 break; 901 default: 902 break; 903 } 904 } 905 i = ++j; 906 907 if (-1 != oproto) { 908 /* remove trailing spaces/tabs, and double-quotes from proto 909 * (note: not unescaping backslash escapes in quoted string) */ 910 v = offsets[oproto+2]; 911 vlen = v + offsets[oproto+3]; 912 while (vlen > v && (s[vlen-1] == ' ' || s[vlen-1] == '\t')) --vlen; 913 if (vlen > v+1 && s[v] == '"' && s[vlen-1] == '"') { ++v; --vlen; } 914 mod_extforward_set_proto(r, s+v, vlen-v); 915 } 916 917 if (p->conf.opts & PROXY_FORWARDED_HOST) { 918 /* Limitations: 919 * - r->http_host is not reset in mod_extforward_restore() 920 * but is currently not an issues since r->http_host will be 921 * reset by next request. If a new module uses r->http_host 922 * in the handle_request_done hook, then should evaluate if that 923 * module should use the forwarded value (probably) or original value. 924 * - due to need to decode and unescape host=..., some extra work is 925 * done in the case where host matches current Host header. 926 * future: might add code to check if Host has actually changed or not 927 * 928 * note: change host after mod_extforward_set_proto() since that may 929 * affect scheme port used in http_request_host_policy() host 930 * normalization 931 */ 932 933 /* find host param set by earliest trusted proxy in proxy chain 934 * (host might be changed anywhere along the chain) */ 935 for (j = i; j < used && -1 == ohost; ) { 936 if (-1 == offsets[j]) { ++j; continue; } 937 if (4 == offsets[j+1] 938 && buffer_eq_icase_ssn(s+offsets[j], "host", 4)) 939 ohost = j; 940 j += 4; /*(k, klen, v, vlen come in sets of 4)*/ 941 } 942 if (-1 != ohost) { 943 if (extforward_check_proxy 944 && !buffer_string_is_empty(r->http_host)) { 945 http_header_env_set(r, 946 CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_HOST"), 947 CONST_BUF_LEN(r->http_host)); 948 } 949 /* remove trailing spaces/tabs, and double-quotes from host */ 950 v = offsets[ohost+2]; 951 vlen = v + offsets[ohost+3]; 952 while (vlen > v && (s[vlen-1] == ' ' || s[vlen-1] == '\t')) --vlen; 953 if (vlen > v+1 && s[v] == '"' && s[vlen-1] == '"') { 954 ++v; --vlen; 955 buffer_copy_string_len(r->http_host, s+v, vlen-v); 956 if (!buffer_backslash_unescape(r->http_host)) { 957 log_error(r->conf.errh, __FILE__, __LINE__, 958 "invalid host= value in Forwarded header"); 959 r->http_status = 400; /* Bad Request */ 960 r->handler_module = NULL; 961 return HANDLER_FINISHED; 962 } 963 } 964 else { 965 buffer_copy_string_len(r->http_host, s+v, vlen-v); 966 } 967 968 if (0 != http_request_host_policy(r->http_host, 969 r->conf.http_parseopts, 970 r->con->proto_default_port)) { 971 /*(reject invalid chars in Host)*/ 972 log_error(r->conf.errh, __FILE__, __LINE__, 973 "invalid host= value in Forwarded header"); 974 r->http_status = 400; /* Bad Request */ 975 r->handler_module = NULL; 976 return HANDLER_FINISHED; 977 } 978 979 config_cond_cache_reset_item(r, COMP_HTTP_HOST); 980 } 981 } 982 983 if (p->conf.opts & PROXY_FORWARDED_REMOTE_USER) { 984 /* find remote_user param set by closest proxy 985 * (auth may have been handled by any trusted proxy in proxy chain) */ 986 for (j = i; j < used; ) { 987 if (-1 == offsets[j]) { ++j; continue; } 988 if (11 == offsets[j+1] 989 && buffer_eq_icase_ssn(s+offsets[j], "remote_user", 11)) 990 oremote_user = j; 991 j += 4; /*(k, klen, v, vlen come in sets of 4)*/ 992 } 993 if (-1 != oremote_user) { 994 /* ???: should we also support param for auth_type ??? */ 995 /* remove trailing spaces/tabs, and double-quotes from remote_user*/ 996 v = offsets[oremote_user+2]; 997 vlen = v + offsets[oremote_user+3]; 998 while (vlen > v && (s[vlen-1] == ' ' || s[vlen-1] == '\t')) --vlen; 999 if (vlen > v+1 && s[v] == '"' && s[vlen-1] == '"') { 1000 buffer *euser; 1001 ++v; --vlen; 1002 http_header_env_set(r, 1003 CONST_STR_LEN("REMOTE_USER"), s+v, vlen-v); 1004 euser = http_header_env_get(r, CONST_STR_LEN("REMOTE_USER")); 1005 force_assert(NULL != euser); 1006 if (!buffer_backslash_unescape(euser)) { 1007 log_error(r->conf.errh, __FILE__, __LINE__, 1008 "invalid remote_user= value in Forwarded header"); 1009 r->http_status = 400; /* Bad Request */ 1010 r->handler_module = NULL; 1011 return HANDLER_FINISHED; 1012 } 1013 } 1014 else { 1015 http_header_env_set(r, 1016 CONST_STR_LEN("REMOTE_USER"), s+v, vlen-v); 1017 } 1018 } 1019 } 1020 1021 #if 0 1022 if ((p->conf.opts & PROXY_FORWARDED_CREATE_XFF) 1023 && !light_btst(r->rqst_htags, HTTP_HEADER_X_FORWARDED_FOR)) { 1024 /* create X-Forwarded-For if not present 1025 * (and at least original connecting IP is a trusted proxy) */ 1026 buffer *xff = r->tmp_buf; 1027 buffer_clear(xff); 1028 for (j = 0; j < used; ) { 1029 if (-1 == offsets[j]) { ++j; continue; } 1030 if (3 == offsets[j+1] 1031 && buffer_eq_icase_ssn(s+offsets[j], "for", 3)) { 1032 if (!buffer_string_is_empty(xff)) 1033 buffer_append_string_len(xff, CONST_STR_LEN(", ")); 1034 /* quoted-string, IPv6 brackets, and :port already removed */ 1035 v = offsets[j+2]; 1036 vlen = offsets[j+3]; 1037 buffer_append_string_len(xff, s+v, vlen); 1038 if (s[v-1] != '=') { /*(must have been quoted-string)*/ 1039 char *x = 1040 memchr(xff->ptr+buffer_string_length(xff)-vlen,'\\',vlen); 1041 if (NULL != x) { /* backslash unescape in-place */ 1042 for (v = 0; x[v]; ++x) { 1043 if (x[v] == '\\' && x[++v] == '\0') 1044 break; /*(invalid trailing backslash)*/ 1045 *x = x[v]; 1046 } 1047 buffer_string_set_length(xff, x - xff->ptr); 1048 } 1049 } 1050 /* skip to next group; take first "for=..." in group 1051 * (should be 0 or 1 "for=..." per group, but not trusted) */ 1052 do { j += 4; } while (-1 != offsets[j]); 1053 ++j; 1054 continue; 1055 } 1056 j += 4; /*(k, klen, v, vlen come in sets of 4)*/ 1057 } 1058 http_header_request_set(r, HTTP_HEADER_X_FORWARDED_FOR, CONST_STR_LEN("X-Forwarded-For"), CONST_BUF_LEN(xff)); 1059 } 1060 #endif 1061 1062 return HANDLER_GO_ON; 1063 } 1064 1065 URIHANDLER_FUNC(mod_extforward_uri_handler) { 1066 plugin_data *p = p_d; 1067 const buffer *forwarded = NULL; 1068 int is_forwarded_header = 0; 1069 1070 mod_extforward_patch_config(r, p); 1071 if (NULL == p->conf.forwarder) return HANDLER_GO_ON; 1072 1073 if (r->conf.log_request_handling) { 1074 log_error(r->conf.errh, __FILE__, __LINE__, 1075 "-- mod_extforward_uri_handler called"); 1076 } 1077 1078 if (p->conf.hap_PROXY_ssl_client_verify) { 1079 const data_string *ds; 1080 handler_ctx *hctx = r->con->plugin_ctx[p->id]; 1081 if (NULL != hctx && hctx->ssl_client_verify && NULL != hctx->env 1082 && NULL != (ds = (const data_string *)array_get_element_klen(hctx->env, CONST_STR_LEN("SSL_CLIENT_S_DN_CN")))) { 1083 http_header_env_set(r, 1084 CONST_STR_LEN("SSL_CLIENT_VERIFY"), 1085 CONST_STR_LEN("SUCCESS")); 1086 http_header_env_set(r, 1087 CONST_STR_LEN("REMOTE_USER"), 1088 CONST_BUF_LEN(&ds->value)); 1089 http_header_env_set(r, 1090 CONST_STR_LEN("AUTH_TYPE"), 1091 CONST_STR_LEN("SSL_CLIENT_VERIFY")); 1092 } else { 1093 http_header_env_set(r, 1094 CONST_STR_LEN("SSL_CLIENT_VERIFY"), 1095 CONST_STR_LEN("NONE")); 1096 } 1097 } 1098 1099 /* Note: headers are parsed per-request even when using HAProxy PROXY 1100 * protocol since Forwarded header might provide additional info and 1101 * internal _L_ vars might be set for later use by mod_proxy or others*/ 1102 /*if (p->conf.hap_PROXY) return HANDLER_GO_ON;*/ 1103 1104 if (NULL == p->conf.headers) return HANDLER_GO_ON; 1105 1106 /* Do not reparse headers for same request, e.g after HANDER_COMEBACK 1107 * from mod_rewrite, mod_magnet MAGNET_RESTART_REQUEST, mod_cgi 1108 * cgi.local-redir, or gw_backend reconnect. This has the implication 1109 * that mod_magnet and mod_cgi with local-redir should not modify 1110 * Forwarded or related headers and expect effects here. */ 1111 handler_ctx *hctx = r->con->plugin_ctx[p->id]; 1112 if (NULL != hctx && NULL != hctx->saved_remote_addr_buf 1113 && hctx->request_count == r->con->request_count) 1114 return HANDLER_GO_ON; 1115 1116 for (uint32_t k = 0; k < p->conf.headers->used; ++k) { 1117 const data_string * const ds = (data_string *)p->conf.headers->data[k]; 1118 const buffer * const hdr = &ds->value; 1119 forwarded = http_header_request_get(r, ds->ext, CONST_BUF_LEN(hdr)); 1120 if (forwarded) { 1121 is_forwarded_header = buffer_is_equal_caseless_string(hdr, CONST_STR_LEN("Forwarded")); 1122 break; 1123 } 1124 } 1125 if (NULL == forwarded) { 1126 if (r->conf.log_request_handling) { 1127 log_error(r->conf.errh, __FILE__, __LINE__, 1128 "no forward header found, skipping"); 1129 } 1130 1131 return HANDLER_GO_ON; 1132 } 1133 1134 /* if the remote ip itself is not trusted, then do nothing */ 1135 if (!is_connection_trusted(r->con, p)) { 1136 if (r->conf.log_request_handling) { 1137 log_error(r->conf.errh, __FILE__, __LINE__, 1138 "remote address %s is NOT a trusted proxy, skipping", 1139 r->con->dst_addr_buf->ptr); 1140 } 1141 1142 return HANDLER_GO_ON; 1143 } 1144 1145 if (is_forwarded_header) { 1146 return mod_extforward_Forwarded(r, p, forwarded); 1147 } 1148 1149 return mod_extforward_X_Forwarded_For(r, p, forwarded); 1150 } 1151 1152 1153 REQUEST_FUNC(mod_extforward_handle_request_env) { 1154 plugin_data *p = p_d; 1155 handler_ctx *hctx = r->con->plugin_ctx[p->id]; 1156 if (NULL == hctx || NULL == hctx->env) return HANDLER_GO_ON; 1157 for (uint32_t i=0; i < hctx->env->used; ++i) { 1158 /* note: replaces values which may have been set by mod_openssl 1159 * (when mod_extforward is listed after mod_openssl in server.modules)*/ 1160 data_string *ds = (data_string *)hctx->env->data[i]; 1161 http_header_env_set(r, 1162 CONST_BUF_LEN(&ds->key), CONST_BUF_LEN(&ds->value)); 1163 } 1164 return HANDLER_GO_ON; 1165 } 1166 1167 1168 REQUEST_FUNC(mod_extforward_restore) { 1169 /* Preserve changed addr for lifetime of h2 connection; upstream proxy 1170 * should not reuse same h2 connection for requests from different clients*/ 1171 if (r->http_version > HTTP_VERSION_1_1) /*(e.g. HTTP_VERSION_2)*/ 1172 return HANDLER_GO_ON; 1173 /* XXX: should change this to not occur at request reset, 1174 * but instead at connection reset */ 1175 plugin_data *p = p_d; 1176 connection * const con = r->con; 1177 handler_ctx *hctx = con->plugin_ctx[p->id]; 1178 1179 if (!hctx) return HANDLER_GO_ON; 1180 1181 if (NULL != hctx->saved_remote_addr_buf) { 1182 con->dst_addr = hctx->saved_remote_addr; 1183 buffer_free(con->dst_addr_buf); 1184 con->dst_addr_buf = hctx->saved_remote_addr_buf; 1185 hctx->saved_remote_addr_buf = NULL; 1186 /* Now, clean the conf_cond cache, because we may have changed the results of tests */ 1187 config_cond_cache_reset_item(r, COMP_HTTP_REMOTE_IP); 1188 } 1189 1190 if (NULL == hctx->env) { 1191 handler_ctx_free(hctx); 1192 con->plugin_ctx[p->id] = NULL; 1193 } 1194 1195 return HANDLER_GO_ON; 1196 } 1197 1198 1199 CONNECTION_FUNC(mod_extforward_handle_con_close) 1200 { 1201 plugin_data *p = p_d; 1202 handler_ctx *hctx = con->plugin_ctx[p->id]; 1203 if (NULL != hctx) { 1204 if (NULL != hctx->saved_network_read) { 1205 con->network_read = hctx->saved_network_read; 1206 } 1207 if (NULL != hctx->saved_remote_addr_buf) { 1208 con->dst_addr = hctx->saved_remote_addr; 1209 buffer_free(con->dst_addr_buf); 1210 con->dst_addr_buf = hctx->saved_remote_addr_buf; 1211 } 1212 if (NULL != hctx->env) { 1213 array_free(hctx->env); 1214 } 1215 handler_ctx_free(hctx); 1216 con->plugin_ctx[p->id] = NULL; 1217 } 1218 1219 return HANDLER_GO_ON; 1220 } 1221 1222 1223 static int mod_extforward_network_read (connection *con, chunkqueue *cq, off_t max_bytes); 1224 1225 CONNECTION_FUNC(mod_extforward_handle_con_accept) 1226 { 1227 request_st * const r = &con->request; 1228 plugin_data *p = p_d; 1229 mod_extforward_patch_config(r, p); 1230 if (!p->conf.hap_PROXY) return HANDLER_GO_ON; 1231 if (NULL == p->conf.forwarder) return HANDLER_GO_ON; 1232 if (is_connection_trusted(con, p)) { 1233 handler_ctx *hctx = handler_ctx_init(); 1234 con->plugin_ctx[p->id] = hctx; 1235 hctx->saved_network_read = con->network_read; 1236 con->network_read = mod_extforward_network_read; 1237 } 1238 else { 1239 if (r->conf.log_request_handling) { 1240 log_error(r->conf.errh, __FILE__, __LINE__, 1241 "remote address %s is NOT a trusted proxy, skipping", 1242 con->dst_addr_buf->ptr); 1243 } 1244 } 1245 return HANDLER_GO_ON; 1246 } 1247 1248 1249 int mod_extforward_plugin_init(plugin *p); 1250 int mod_extforward_plugin_init(plugin *p) { 1251 p->version = LIGHTTPD_VERSION_ID; 1252 p->name = "extforward"; 1253 1254 p->init = mod_extforward_init; 1255 p->handle_connection_accept = mod_extforward_handle_con_accept; 1256 p->handle_uri_raw = mod_extforward_uri_handler; 1257 p->handle_request_env = mod_extforward_handle_request_env; 1258 p->handle_request_done = mod_extforward_restore; 1259 p->handle_request_reset = mod_extforward_restore; 1260 p->handle_connection_close = mod_extforward_handle_con_close; 1261 p->set_defaults = mod_extforward_set_defaults; 1262 p->cleanup = mod_extforward_free; 1263 1264 return 0; 1265 } 1266 1267 1268 1269 1270 /* Modified from: 1271 * http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt 1272 * 1273 9. Sample code 1274 1275 The code below is an example of how a receiver may deal with both versions of 1276 the protocol header for TCP over IPv4 or IPv6. The function is supposed to be 1277 called upon a read event. Addresses may be directly copied into their final 1278 memory location since they're transported in network byte order. The sending 1279 side is even simpler and can easily be deduced from this sample code. 1280 * 1281 */ 1282 1283 union hap_PROXY_hdr { 1284 struct { 1285 char line[108]; 1286 } v1; 1287 struct { 1288 uint8_t sig[12]; 1289 uint8_t ver_cmd; 1290 uint8_t fam; 1291 uint16_t len; 1292 union { 1293 struct { /* for TCP/UDP over IPv4, len = 12 */ 1294 uint32_t src_addr; 1295 uint32_t dst_addr; 1296 uint16_t src_port; 1297 uint16_t dst_port; 1298 } ip4; 1299 struct { /* for TCP/UDP over IPv6, len = 36 */ 1300 uint8_t src_addr[16]; 1301 uint8_t dst_addr[16]; 1302 uint16_t src_port; 1303 uint16_t dst_port; 1304 } ip6; 1305 struct { /* for AF_UNIX sockets, len = 216 */ 1306 uint8_t src_addr[108]; 1307 uint8_t dst_addr[108]; 1308 } unx; 1309 } addr; 1310 } v2; 1311 }; 1312 1313 /* 1314 If the length specified in the PROXY protocol header indicates that additional 1315 bytes are part of the header beyond the address information, a receiver may 1316 choose to skip over and ignore those bytes, or attempt to interpret those 1317 bytes. 1318 1319 The information in those bytes will be arranged in Type-Length-Value (TLV 1320 vectors) in the following format. The first byte is the Type of the vector. 1321 The second two bytes represent the length in bytes of the value (not included 1322 the Type and Length bytes), and following the length field is the number of 1323 bytes specified by the length. 1324 */ 1325 struct pp2_tlv { 1326 uint8_t type; 1327 uint8_t length_hi; 1328 uint8_t length_lo; 1329 /*uint8_t value[0];*//* C99 zero-length array */ 1330 }; 1331 1332 /* 1333 The following types have already been registered for the <type> field : 1334 */ 1335 1336 #define PP2_TYPE_ALPN 0x01 1337 #define PP2_TYPE_AUTHORITY 0x02 1338 #define PP2_TYPE_CRC32C 0x03 1339 #define PP2_TYPE_NOOP 0x04 1340 #define PP2_TYPE_SSL 0x20 1341 #define PP2_SUBTYPE_SSL_VERSION 0x21 1342 #define PP2_SUBTYPE_SSL_CN 0x22 1343 #define PP2_SUBTYPE_SSL_CIPHER 0x23 1344 #define PP2_SUBTYPE_SSL_SIG_ALG 0x24 1345 #define PP2_SUBTYPE_SSL_KEY_ALG 0x25 1346 #define PP2_TYPE_NETNS 0x30 1347 1348 /* 1349 For the type PP2_TYPE_SSL, the value is itselv a defined like this : 1350 */ 1351 1352 struct pp2_tlv_ssl { 1353 uint8_t client; 1354 uint32_t verify; 1355 /*struct pp2_tlv sub_tlv[0];*//* C99 zero-length array */ 1356 }; 1357 1358 /* 1359 And the <client> field is made of a bit field from the following values, 1360 indicating which element is present : 1361 */ 1362 1363 #define PP2_CLIENT_SSL 0x01 1364 #define PP2_CLIENT_CERT_CONN 0x02 1365 #define PP2_CLIENT_CERT_SESS 0x04 1366 1367 1368 1369 1370 #ifndef MSG_DONTWAIT 1371 #define MSG_DONTWAIT 0 1372 #endif 1373 #ifndef MSG_NOSIGNAL 1374 #define MSG_NOSIGNAL 0 1375 #endif 1376 1377 /* returns 0 if needs to poll, <0 upon error or >0 is protocol vers (success) */ 1378 static int hap_PROXY_recv (const int fd, union hap_PROXY_hdr * const hdr, const int family, const int so_type) 1379 { 1380 static const char v2sig[12] = 1381 "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"; 1382 1383 ssize_t ret; 1384 size_t sz; 1385 int ver; 1386 1387 do { 1388 ret = recv(fd, hdr, sizeof(*hdr), MSG_PEEK|MSG_DONTWAIT|MSG_NOSIGNAL); 1389 } while (-1 == ret && errno == EINTR); 1390 1391 if (-1 == ret) 1392 return (errno == EAGAIN 1393 #ifdef EWOULDBLOCK 1394 #if EAGAIN != EWOULDBLOCK 1395 || errno == EWOULDBLOCK 1396 #endif 1397 #endif 1398 ) ? 0 : -1; 1399 1400 if (ret >= 16 && 0 == memcmp(&hdr->v2, v2sig, 12) 1401 && (hdr->v2.ver_cmd & 0xF0) == 0x20) { 1402 ver = 2; 1403 sz = 16 + (size_t)ntohs(hdr->v2.len); 1404 if ((size_t)ret < sz) 1405 return -2; /* truncated or too large header */ 1406 1407 switch (hdr->v2.ver_cmd & 0xF) { 1408 case 0x01: break; /* PROXY command */ 1409 case 0x00: break; /* LOCAL command */ 1410 default: return -2; /* not a supported command */ 1411 } 1412 } 1413 else if (ret >= 8 && 0 == memcmp(hdr->v1.line, "PROXY", 5)) { 1414 const char *end = memchr(hdr->v1.line, '\r', ret - 1); 1415 if (!end || end[1] != '\n') 1416 return -2; /* partial or invalid header */ 1417 ver = 1; 1418 sz = (size_t)(end + 2 - hdr->v1.line); /* skip header + CRLF */ 1419 } 1420 else { 1421 /* Wrong protocol */ 1422 return -2; 1423 } 1424 1425 /* we need to consume the appropriate amount of data from the socket 1426 * (overwrites existing contents of hdr with same data) */ 1427 UNUSED(family); 1428 UNUSED(so_type); 1429 do { 1430 #if defined(MSG_TRUNC) && defined(__linux__) 1431 if ((family==AF_INET || family==AF_INET6) && so_type == SOCK_STREAM) { 1432 ret = recv(fd, hdr, sz, MSG_TRUNC|MSG_DONTWAIT|MSG_NOSIGNAL); 1433 if (ret >= 0 || errno != EINVAL) continue; 1434 } 1435 #endif 1436 ret = recv(fd, hdr, sz, MSG_DONTWAIT|MSG_NOSIGNAL); 1437 } while (-1 == ret && errno == EINTR); 1438 if (ret < 0) return -1; 1439 if (ret != (ssize_t)sz) { 1440 errno = EIO; /*(partial read; valid but unexpected; not handled)*/ 1441 return -1; 1442 } 1443 if (1 == ver) hdr->v1.line[sz-2] = '\0'; /*terminate str to ease parsing*/ 1444 return ver; 1445 } 1446 1447 1448 __attribute_pure__ 1449 static int mod_extforward_str_to_port (const char * const s) 1450 { 1451 /*(more strict than strtol(); digits only)*/ 1452 int port = 0; 1453 for (int i = 0; i < 5; ++i, port *= 10) { 1454 if (!light_isdigit(s[i])) return -1; 1455 port += (s[i] - '0'); 1456 if (s[i+1] == '\0') return port; 1457 } 1458 return -1; 1459 } 1460 1461 /* coverity[-tainted_data_sink: arg-1] */ 1462 static int mod_extforward_hap_PROXY_v1 (connection * const con, 1463 union hap_PROXY_hdr * const hdr) 1464 { 1465 #ifdef __COVERITY__ 1466 __coverity_tainted_data_sink__(hdr); 1467 #endif 1468 1469 /* samples 1470 * "PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535\r\n" 1471 * "PROXY TCP6 ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n" 1472 * "PROXY UNKNOWN\r\n" 1473 * "PROXY UNKNOWN ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n" 1474 */ 1475 char *s = hdr->v1.line + sizeof("PROXY")-1; /*checked in hap_PROXY_recv()*/ 1476 char *src_addr, *dst_addr, *src_port, *dst_port; 1477 int family; 1478 int src_lport, dst_lport; 1479 if (*s != ' ') return -1; 1480 ++s; 1481 if (s[0] == 'T' && s[1] == 'C' && s[2] == 'P' && s[4] == ' ') { 1482 if (s[3] == '4') { 1483 family = AF_INET; 1484 } else if (s[3] == '6') { 1485 family = AF_INET6; 1486 } 1487 else { 1488 return -1; 1489 } 1490 s += 5; 1491 } 1492 else if (0 == memcmp(s, "UNKNOWN", sizeof("UNKNOWN")-1) 1493 && (s[7] == '\0' || s[7] == ' ')) { 1494 return 0; /* keep local connection address */ 1495 } 1496 else { 1497 return -1; 1498 } 1499 1500 /*(strsep() should be fairly portable, but is not standard)*/ 1501 src_addr = s; 1502 dst_addr = strchr(src_addr, ' '); 1503 if (NULL == dst_addr) return -1; 1504 *dst_addr++ = '\0'; 1505 src_port = strchr(dst_addr, ' '); 1506 if (NULL == src_port) return -1; 1507 *src_port++ = '\0'; 1508 dst_port = strchr(src_port, ' '); 1509 if (NULL == dst_port) return -1; 1510 *dst_port++ = '\0'; 1511 1512 src_lport = mod_extforward_str_to_port(src_port); 1513 if (src_lport <= 0) return -1; 1514 dst_lport = mod_extforward_str_to_port(dst_port); 1515 if (dst_lport <= 0) return -1; 1516 1517 if (1 != sock_addr_inet_pton(&con->dst_addr, 1518 src_addr, family, (unsigned short)src_lport)) 1519 return -1; 1520 /* Forwarded by=... could be saved here. 1521 * (see additional comments in mod_extforward_hap_PROXY_v2()) */ 1522 1523 /* re-parse addr to string to normalize 1524 * (instead of trusting PROXY to provide canonicalized src_addr string) 1525 * (should prefer PROXY v2 protocol if concerned about performance) */ 1526 sock_addr_inet_ntop_copy_buffer(con->dst_addr_buf, &con->dst_addr); 1527 1528 return 0; 1529 } 1530 1531 1532 /* coverity[-tainted_data_sink: arg-1] */ 1533 static int mod_extforward_hap_PROXY_v2 (connection * const con, 1534 union hap_PROXY_hdr * const hdr) 1535 { 1536 #ifdef __COVERITY__ 1537 __coverity_tainted_data_sink__(hdr); 1538 #endif 1539 1540 /* If HAProxy-PROXY protocol used, then lighttpd acts as transparent proxy, 1541 * masquerading as servicing the client IP provided in by HAProxy-PROXY hdr. 1542 * The connecting con->dst_addr and con->dst_addr_buf are not saved here, 1543 * so that info is lost unless getsockname() and getpeername() are used. 1544 * One result is that mod_proxy will use the masqueraded IP instead of the 1545 * actual IP when updated Forwarded and X-Forwarded-For (but if actual 1546 * connection IPs needed, better to save the info here rather than use 1547 * syscalls to retrieve the info later). 1548 * (Exception: con->dst_addr can be further changed if mod_extforward parses 1549 * Forwarded or X-Forwarded-For request headers later, after request headers 1550 * have been received.) 1551 */ 1552 1553 /* Forwarded by=... could be saved here. The by param is for backends to be 1554 * able to construct URIs for that interface (interface on server which 1555 * received request and made PROXY connection here), though that server 1556 * should provide that information in updated Forwarded or X-Forwarded-For 1557 * HTTP headers */ 1558 /*struct sockaddr_storage by;*/ 1559 1560 /* Addresses provided by HAProxy-PROXY protocol are in network byte order. 1561 * Note: addr info is not validated, so do not accept HAProxy-PROXY 1562 * protocol from untrusted servers. For example, untrusted servers from 1563 * which HAProxy-PROXY protocol is accepted (don't do that) could pretend 1564 * to be from the internal network and might thereby bypass security policy. 1565 */ 1566 1567 /* (Clear con->dst_addr with memset() in case actual and proxies IPs 1568 * are different domains, e.g. one is IPv4 and the other is IPv6) */ 1569 1570 struct pp2_tlv *tlv; 1571 uint32_t sz = ntohs(hdr->v2.len); 1572 uint32_t len = 0; 1573 1574 switch (hdr->v2.ver_cmd & 0xF) { 1575 case 0x01: break; /* PROXY command */ 1576 case 0x00: return 0;/* LOCAL command; keep local connection address */ 1577 default: return -1;/* should not happen; validated in hap_PROXY_recv()*/ 1578 } 1579 1580 /* PROXY command */ 1581 1582 switch (hdr->v2.fam) { 1583 case 0x11: /* TCPv4 */ 1584 sock_addr_assign(&con->dst_addr, AF_INET, hdr->v2.addr.ip4.src_port, 1585 &hdr->v2.addr.ip4.src_addr); 1586 sock_addr_inet_ntop_copy_buffer(con->dst_addr_buf, &con->dst_addr); 1587 #if 0 1588 ((struct sockaddr_in *)&by)->sin_family = AF_INET; 1589 ((struct sockaddr_in *)&by)->sin_addr.s_addr = 1590 hdr->v2.addr.ip4.dst_addr; 1591 ((struct sockaddr_in *)&by)->sin_port = 1592 hdr->v2.addr.ip4.dst_port; 1593 #endif 1594 len = (uint32_t)sizeof(hdr->v2.addr.ip4); 1595 break; 1596 #ifdef HAVE_IPV6 1597 case 0x21: /* TCPv6 */ 1598 sock_addr_assign(&con->dst_addr, AF_INET6, hdr->v2.addr.ip6.src_port, 1599 &hdr->v2.addr.ip6.src_addr); 1600 sock_addr_inet_ntop_copy_buffer(con->dst_addr_buf, &con->dst_addr); 1601 #if 0 1602 ((struct sockaddr_in6 *)&by)->sin6_family = AF_INET6; 1603 memcpy(&((struct sockaddr_in6 *)&by)->sin6_addr, 1604 hdr->v2.addr.ip6.dst_addr, 16); 1605 ((struct sockaddr_in6 *)&by)->sin6_port = 1606 hdr->v2.addr.ip6.dst_port; 1607 #endif 1608 len = (uint32_t)sizeof(hdr->v2.addr.ip6); 1609 break; 1610 #endif 1611 #ifdef HAVE_SYS_UN_H 1612 case 0x31: /* UNIX domain socket */ 1613 { 1614 char *src_addr = (char *)hdr->v2.addr.unx.src_addr; 1615 char *z = memchr(src_addr, '\0', UNIX_PATH_MAX); 1616 if (NULL == z) return -1; /* invalid addr; too long */ 1617 len = (uint32_t)(z - src_addr + 1); /*(+1 for '\0')*/ 1618 sock_addr_assign(&con->dst_addr, AF_UNIX, 0, src_addr); 1619 buffer_copy_string_len(con->dst_addr_buf, src_addr, len); 1620 } 1621 #if 0 /*(dst_addr should be identical to src_addr for AF_UNIX)*/ 1622 ((struct sockaddr_un *)&by)->sun_family = AF_UNIX; 1623 memcpy(&((struct sockaddr_un *)&by)->sun_path, 1624 hdr->v2.addr.unx.dst_addr, 108); 1625 #endif 1626 len = (uint32_t)sizeof(hdr->v2.addr.unx); 1627 break; 1628 #endif 1629 default: /* keep local connection address; unsupported protocol */ 1630 return 0; 1631 } 1632 1633 /* (optional) Type-Length-Value (TLV vectors) follow addresses */ 1634 1635 tlv = (struct pp2_tlv *)((char *)hdr + 16); 1636 for (sz -= len, len -= 3; sz >= 3; sz -= 3 + len) { 1637 tlv = (struct pp2_tlv *)((char *)tlv + 3 + len); 1638 len = ((uint32_t)tlv->length_hi << 8) | tlv->length_lo; 1639 if (3 + len > sz) break; /*(invalid TLV)*/ 1640 switch (tlv->type) { 1641 #if 0 /*(not implemented here)*/ 1642 case PP2_TYPE_ALPN: 1643 case PP2_TYPE_AUTHORITY: 1644 case PP2_TYPE_CRC32C: 1645 #endif 1646 case PP2_TYPE_SSL: { 1647 static const uint32_t zero = 0; 1648 handler_ctx *hctx = 1649 con->plugin_ctx[mod_extforward_plugin_data_singleton->id]; 1650 struct pp2_tlv_ssl *tlv_ssl = 1651 (struct pp2_tlv_ssl *)(void *)((char *)tlv+3); 1652 struct pp2_tlv *subtlv = tlv; 1653 if (tlv_ssl->client & PP2_CLIENT_SSL) { 1654 con->proto_default_port = 443; /* "https" */ 1655 } 1656 if ((tlv_ssl->client & (PP2_CLIENT_CERT_CONN|PP2_CLIENT_CERT_SESS)) 1657 && 0 == memcmp(&tlv_ssl->verify, &zero, 4)) { /* misaligned */ 1658 hctx->ssl_client_verify = 1; 1659 } 1660 for (uint32_t subsz = len-5, n = 5; subsz >= 3; subsz -= 3 + n) { 1661 subtlv = (struct pp2_tlv *)((char *)subtlv + 3 + n); 1662 n = ((uint32_t)subtlv->length_hi << 8) | subtlv->length_lo; 1663 if (3 + n > subsz) break; /*(invalid TLV)*/ 1664 if (NULL == hctx->env) hctx->env = array_init(8); 1665 switch (subtlv->type) { 1666 case PP2_SUBTYPE_SSL_VERSION: 1667 array_set_key_value(hctx->env, 1668 CONST_STR_LEN("SSL_PROTOCOL"), 1669 (char *)subtlv+3, n); 1670 break; 1671 case PP2_SUBTYPE_SSL_CN: 1672 /* (tlv_ssl->client & PP2_CLIENT_CERT_CONN) 1673 * or 1674 * (tlv_ssl->client & PP2_CLIENT_CERT_SESS) */ 1675 array_set_key_value(hctx->env, 1676 CONST_STR_LEN("SSL_CLIENT_S_DN_CN"), 1677 (char *)subtlv+3, n); 1678 break; 1679 case PP2_SUBTYPE_SSL_CIPHER: 1680 array_set_key_value(hctx->env, 1681 CONST_STR_LEN("SSL_CIPHER"), 1682 (char *)subtlv+3, n); 1683 break; 1684 case PP2_SUBTYPE_SSL_SIG_ALG: 1685 array_set_key_value(hctx->env, 1686 CONST_STR_LEN("SSL_SERVER_A_SIG"), 1687 (char *)subtlv+3, n); 1688 break; 1689 case PP2_SUBTYPE_SSL_KEY_ALG: 1690 array_set_key_value(hctx->env, 1691 CONST_STR_LEN("SSL_SERVER_A_KEY"), 1692 (char *)subtlv+3, n); 1693 break; 1694 default: 1695 break; 1696 } 1697 } 1698 break; 1699 } 1700 #if 0 /*(not implemented here)*/ 1701 case PP2_TYPE_NETNS: 1702 #endif 1703 /*case PP2_TYPE_NOOP:*//* no-op */ 1704 default: 1705 break; 1706 } 1707 } 1708 1709 return 0; 1710 } 1711 1712 1713 static int mod_extforward_network_read (connection *con, 1714 chunkqueue *cq, off_t max_bytes) 1715 { 1716 /* XXX: when using hap-PROXY protocol, currently avoid overhead of setting 1717 * _L_ environment variables for mod_proxy to accurately set Forwarded hdr 1718 * In the future, might add config switch to enable doing this extra work */ 1719 1720 union hap_PROXY_hdr hdr; 1721 log_error_st *errh; 1722 const int family = sock_addr_get_family(&con->dst_addr); 1723 int rc = hap_PROXY_recv(con->fd, &hdr, family, SOCK_STREAM); 1724 switch (rc) { 1725 case 2: rc = mod_extforward_hap_PROXY_v2(con, &hdr); break; 1726 case 1: rc = mod_extforward_hap_PROXY_v1(con, &hdr); break; 1727 case 0: return 0; /*(errno == EAGAIN || errno == EWOULDBLOCK)*/ 1728 case -1: errh = con->srv->errh; 1729 log_perror(errh,__FILE__,__LINE__,"hap-PROXY recv()"); 1730 rc = -1; break; 1731 case -2: errh = con->srv->errh; 1732 log_error(errh,__FILE__,__LINE__, 1733 "hap-PROXY proto received invalid/unsupported request"); 1734 /* fall through */ 1735 default: rc = -1; break; 1736 } 1737 1738 handler_ctx *hctx = 1739 con->plugin_ctx[mod_extforward_plugin_data_singleton->id]; 1740 con->network_read = hctx->saved_network_read; 1741 hctx->saved_network_read = NULL; 1742 return (0 == rc) ? con->network_read(con, cq, max_bytes) : rc; 1743 } 1744