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