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