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_is_equal_caseless_string(&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 static int find_end_quoted_string (const char * const s, int i) { 656 do { 657 ++i; 658 } while (s[i] != '"' && s[i] != '\0' && (s[i] != '\\' || s[++i] != '\0')); 659 return i; 660 } 661 662 static int find_next_semicolon_or_comma_or_eq (const char * const s, int i) { 663 for (; s[i] != '=' && s[i] != ';' && s[i] != ',' && s[i] != '\0'; ++i) { 664 if (s[i] == '"') { 665 i = find_end_quoted_string(s, i); 666 if (s[i] == '\0') return -1; 667 } 668 } 669 return i; 670 } 671 672 static int find_next_semicolon_or_comma (const char * const s, int i) { 673 for (; s[i] != ';' && s[i] != ',' && s[i] != '\0'; ++i) { 674 if (s[i] == '"') { 675 i = find_end_quoted_string(s, i); 676 if (s[i] == '\0') return -1; 677 } 678 } 679 return i; 680 } 681 682 static int buffer_backslash_unescape (buffer * const b) { 683 /* (future: might move to buffer.c) */ 684 size_t j = 0; 685 size_t len = buffer_string_length(b); 686 char *p = memchr(b->ptr, '\\', len); 687 688 if (NULL == p) return 1; /*(nothing to do)*/ 689 690 len -= (size_t)(p - b->ptr); 691 for (size_t i = 0; i < len; ++i) { 692 if (p[i] == '\\') { 693 if (++i == len) return 0; /*(invalid trailing backslash)*/ 694 } 695 p[j++] = p[i]; 696 } 697 buffer_string_set_length(b, (size_t)(p+j - b->ptr)); 698 return 1; 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_string_length(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))) 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 log_error(r->conf.errh, __FILE__, __LINE__, 738 "invalid quoted-string in Forwarded header"); 739 r->http_status = 400; /* Bad Request */ 740 r->handler_module = NULL; 741 return HANDLER_FINISHED; 742 } 743 if (s[i] != '=') continue; 744 klen = i - k; 745 v = ++i; 746 i = find_next_semicolon_or_comma(s, i); 747 if (i < 0) { 748 /*(reject IP spoofing if attacker sets improper quoted-string)*/ 749 log_error(r->conf.errh, __FILE__, __LINE__, 750 "invalid quoted-string in Forwarded header"); 751 r->http_status = 400; /* Bad Request */ 752 r->handler_module = NULL; 753 return HANDLER_FINISHED; 754 } 755 vlen = i - v; /* might be 0 */ 756 757 /* have k, klen, v, vlen 758 * (might contain quoted string) (contents not validated or decoded) 759 * (might be repeated k) 760 */ 761 if (0 == klen) continue; /* invalid k */ 762 if (j >= (int)(sizeof(offsets)/sizeof(int))-4) break; 763 offsets[j+1] = k; 764 offsets[j+2] = klen; 765 offsets[j+3] = v; 766 offsets[j+4] = vlen; 767 j += 4; 768 } 769 770 if (j >= (int)(sizeof(offsets)/sizeof(int))-4) { 771 /* error processing Forwarded; too many params; fail closed */ 772 log_error(r->conf.errh, __FILE__, __LINE__, 773 "Too many params in Forwarded header"); 774 r->http_status = 400; /* Bad Request */ 775 r->handler_module = NULL; 776 return HANDLER_FINISHED; 777 } 778 779 if (-1 == j) return HANDLER_GO_ON; /* make no changes */ 780 used = j+1; 781 offsets[used] = -1; /* mark end of last set of params */ 782 783 while (j >= 4) { /*(param=value pairs)*/ 784 if (-1 == offsets[j]) { --j; continue; } 785 do { 786 j -= 3; /*(k, klen, v, vlen come in sets of 4)*/ 787 } while ((3 != offsets[j+1] /* 3 == sizeof("for")-1 */ 788 || !buffer_eq_icase_ssn(s+offsets[j], "for", 3)) 789 && 0 != j-- && -1 != offsets[j]); 790 if (j < 0) break; 791 if (-1 == offsets[j]) { --j; continue; } 792 793 /* remove trailing spaces/tabs and double-quotes from string 794 * (note: not unescaping backslash escapes in quoted string) */ 795 v = offsets[j+2]; 796 vlen = v + offsets[j+3]; 797 while (vlen > v && (s[vlen-1] == ' ' || s[vlen-1] == '\t')) --vlen; 798 if (vlen > v+1 && s[v] == '"' && s[vlen-1] == '"') { 799 offsets[j+2] = ++v; 800 --vlen; 801 if (s[v] == '[') { 802 /* remove "[]" surrounding IPv6, as well as (optional) port 803 * (assumes properly formatted IPv6 addr from trusted proxy) */ 804 ++v; 805 do { --vlen; } while (vlen > v && s[vlen] != ']'); 806 if (v == vlen) { 807 log_error(r->conf.errh, __FILE__, __LINE__, 808 "Invalid IPv6 addr in Forwarded header"); 809 r->http_status = 400; /* Bad Request */ 810 r->handler_module = NULL; 811 return HANDLER_FINISHED; 812 } 813 } 814 else if (s[v] != '_' && s[v] != '/' && s[v] != 'u') { 815 /* remove (optional) port from non-obfuscated IPv4 */ 816 for (klen=vlen, vlen=v; vlen < klen && s[vlen] != ':'; ++vlen) ; 817 } 818 offsets[j+2] = v; 819 } 820 offsets[j+3] = vlen - v; 821 822 /* obfuscated ipstr and obfuscated port are also accepted here, as 823 * is path to unix domain socket, but note that backslash escapes 824 * in quoted-string were not unescaped above. Also, if obfuscated 825 * identifiers are rotated by proxies as recommended by RFC, then 826 * maintaining list of trusted identifiers is non-trivial and is not 827 * attempted by this module. */ 828 829 if (v != vlen) { 830 int trusted = is_proxy_trusted(p, s+v, vlen-v); 831 832 if (s[v] != '_' && s[v] != '/' 833 && (7 != (vlen - v) || 0 != memcmp(s+v, "unknown", 7))) { 834 ofor = j; /* save most recent non-obfuscated ipstr */ 835 } 836 837 if (!trusted) break; 838 } 839 840 do { --j; } while (j > 0 && -1 != offsets[j]); 841 if (j <= 0) break; 842 --j; 843 } 844 845 if (-1 != ofor) { 846 /* C funcs getaddrinfo(), inet_addr() require '\0'-terminated IP str */ 847 char *ipend = s+offsets[ofor+2]+offsets[ofor+3]; 848 char c = *ipend; 849 int rc; 850 *ipend = '\0'; 851 rc = mod_extforward_set_addr(r, p, s+offsets[ofor+2]); 852 *ipend = c; 853 if (!rc) return HANDLER_GO_ON; /* invalid addr; make no changes */ 854 } 855 else { 856 return HANDLER_GO_ON; /* make no changes */ 857 } 858 859 /* parse out params associated with for=<ip> addr set above */ 860 oproto = ohost = oby = oremote_user = -1; 861 UNUSED(oby); 862 j = ofor; 863 if (j > 0) { do { --j; } while (j > 0 && -1 != offsets[j]); } 864 if (-1 == offsets[j]) ++j; 865 if (j == ofor) j += 4; 866 for (; -1 != offsets[j]; j+=4) { /*(k, klen, v, vlen come in sets of 4)*/ 867 switch (offsets[j+1]) { 868 #if 0 869 case 2: 870 if (buffer_eq_icase_ssn(s+offsets[j], "by", 2)) 871 oby = j; 872 break; 873 #endif 874 #if 0 875 /*(already handled above to find IP prior to earliest trusted proxy)*/ 876 case 3: 877 if (buffer_eq_icase_ssn(s+offsets[j], "for", 3)) 878 ofor = j; 879 break; 880 #endif 881 case 4: 882 if (buffer_eq_icase_ssn(s+offsets[j], "host", 4)) 883 ohost = j; 884 break; 885 case 5: 886 if (buffer_eq_icase_ssn(s+offsets[j], "proto", 5)) 887 oproto = j; 888 break; 889 case 11: 890 if (buffer_eq_icase_ssn(s+offsets[j], "remote_user", 11)) 891 oremote_user = j; 892 break; 893 default: 894 break; 895 } 896 } 897 i = ++j; 898 899 if (-1 != oproto) { 900 /* remove trailing spaces/tabs, and double-quotes from proto 901 * (note: not unescaping backslash escapes in quoted string) */ 902 v = offsets[oproto+2]; 903 vlen = v + offsets[oproto+3]; 904 while (vlen > v && (s[vlen-1] == ' ' || s[vlen-1] == '\t')) --vlen; 905 if (vlen > v+1 && s[v] == '"' && s[vlen-1] == '"') { ++v; --vlen; } 906 mod_extforward_set_proto(r, s+v, vlen-v); 907 } 908 909 if (p->conf.opts & PROXY_FORWARDED_HOST) { 910 /* Limitations: 911 * - r->http_host is not reset in mod_extforward_restore() 912 * but is currently not an issues since r->http_host will be 913 * reset by next request. If a new module uses r->http_host 914 * in the handle_request_done hook, then should evaluate if that 915 * module should use the forwarded value (probably) or original value. 916 * - due to need to decode and unescape host=..., some extra work is 917 * done in the case where host matches current Host header. 918 * future: might add code to check if Host has actually changed or not 919 * 920 * note: change host after mod_extforward_set_proto() since that may 921 * affect scheme port used in http_request_host_policy() host 922 * normalization 923 */ 924 925 /* find host param set by earliest trusted proxy in proxy chain 926 * (host might be changed anywhere along the chain) */ 927 for (j = i; j < used && -1 == ohost; ) { 928 if (-1 == offsets[j]) { ++j; continue; } 929 if (4 == offsets[j+1] 930 && buffer_eq_icase_ssn(s+offsets[j], "host", 4)) 931 ohost = j; 932 j += 4; /*(k, klen, v, vlen come in sets of 4)*/ 933 } 934 if (-1 != ohost) { 935 if (extforward_check_proxy 936 && !buffer_string_is_empty(r->http_host)) { 937 http_header_env_set(r, 938 CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_HOST"), 939 CONST_BUF_LEN(r->http_host)); 940 } 941 /* remove trailing spaces/tabs, and double-quotes from host */ 942 v = offsets[ohost+2]; 943 vlen = v + offsets[ohost+3]; 944 while (vlen > v && (s[vlen-1] == ' ' || s[vlen-1] == '\t')) --vlen; 945 if (vlen > v+1 && s[v] == '"' && s[vlen-1] == '"') { 946 ++v; --vlen; 947 buffer_copy_string_len(r->http_host, s+v, vlen-v); 948 if (!buffer_backslash_unescape(r->http_host)) { 949 log_error(r->conf.errh, __FILE__, __LINE__, 950 "invalid host= value in Forwarded header"); 951 r->http_status = 400; /* Bad Request */ 952 r->handler_module = NULL; 953 return HANDLER_FINISHED; 954 } 955 } 956 else { 957 buffer_copy_string_len(r->http_host, s+v, vlen-v); 958 } 959 960 if (0 != http_request_host_policy(r->http_host, 961 r->conf.http_parseopts, 962 r->con->proto_default_port)) { 963 /*(reject invalid chars in Host)*/ 964 log_error(r->conf.errh, __FILE__, __LINE__, 965 "invalid host= value in Forwarded header"); 966 r->http_status = 400; /* Bad Request */ 967 r->handler_module = NULL; 968 return HANDLER_FINISHED; 969 } 970 971 config_cond_cache_reset_item(r, COMP_HTTP_HOST); 972 } 973 } 974 975 if (p->conf.opts & PROXY_FORWARDED_REMOTE_USER) { 976 /* find remote_user param set by closest proxy 977 * (auth may have been handled by any trusted proxy in proxy chain) */ 978 for (j = i; j < used; ) { 979 if (-1 == offsets[j]) { ++j; continue; } 980 if (11 == offsets[j+1] 981 && buffer_eq_icase_ssn(s+offsets[j], "remote_user", 11)) 982 oremote_user = j; 983 j += 4; /*(k, klen, v, vlen come in sets of 4)*/ 984 } 985 if (-1 != oremote_user) { 986 /* ???: should we also support param for auth_type ??? */ 987 /* remove trailing spaces/tabs, and double-quotes from remote_user*/ 988 v = offsets[oremote_user+2]; 989 vlen = v + offsets[oremote_user+3]; 990 while (vlen > v && (s[vlen-1] == ' ' || s[vlen-1] == '\t')) --vlen; 991 if (vlen > v+1 && s[v] == '"' && s[vlen-1] == '"') { 992 buffer *euser; 993 ++v; --vlen; 994 http_header_env_set(r, 995 CONST_STR_LEN("REMOTE_USER"), s+v, vlen-v); 996 euser = http_header_env_get(r, CONST_STR_LEN("REMOTE_USER")); 997 force_assert(NULL != euser); 998 if (!buffer_backslash_unescape(euser)) { 999 log_error(r->conf.errh, __FILE__, __LINE__, 1000 "invalid remote_user= value in Forwarded header"); 1001 r->http_status = 400; /* Bad Request */ 1002 r->handler_module = NULL; 1003 return HANDLER_FINISHED; 1004 } 1005 } 1006 else { 1007 http_header_env_set(r, 1008 CONST_STR_LEN("REMOTE_USER"), s+v, vlen-v); 1009 } 1010 } 1011 } 1012 1013 #if 0 1014 if ((p->conf.opts & PROXY_FORWARDED_CREATE_XFF) 1015 && !light_btst(r->rqst_htags, HTTP_HEADER_X_FORWARDED_FOR)) { 1016 /* create X-Forwarded-For if not present 1017 * (and at least original connecting IP is a trusted proxy) */ 1018 buffer *xff = r->tmp_buf; 1019 buffer_clear(xff); 1020 for (j = 0; j < used; ) { 1021 if (-1 == offsets[j]) { ++j; continue; } 1022 if (3 == offsets[j+1] 1023 && buffer_eq_icase_ssn(s+offsets[j], "for", 3)) { 1024 if (!buffer_string_is_empty(xff)) 1025 buffer_append_string_len(xff, CONST_STR_LEN(", ")); 1026 /* quoted-string, IPv6 brackets, and :port already removed */ 1027 v = offsets[j+2]; 1028 vlen = offsets[j+3]; 1029 buffer_append_string_len(xff, s+v, vlen); 1030 if (s[v-1] != '=') { /*(must have been quoted-string)*/ 1031 char *x = 1032 memchr(xff->ptr+buffer_string_length(xff)-vlen,'\\',vlen); 1033 if (NULL != x) { /* backslash unescape in-place */ 1034 for (v = 0; x[v]; ++x) { 1035 if (x[v] == '\\' && x[++v] == '\0') 1036 break; /*(invalid trailing backslash)*/ 1037 *x = x[v]; 1038 } 1039 buffer_string_set_length(xff, x - xff->ptr); 1040 } 1041 } 1042 /* skip to next group; take first "for=..." in group 1043 * (should be 0 or 1 "for=..." per group, but not trusted) */ 1044 do { j += 4; } while (-1 != offsets[j]); 1045 ++j; 1046 continue; 1047 } 1048 j += 4; /*(k, klen, v, vlen come in sets of 4)*/ 1049 } 1050 http_header_request_set(r, HTTP_HEADER_X_FORWARDED_FOR, CONST_STR_LEN("X-Forwarded-For"), CONST_BUF_LEN(xff)); 1051 } 1052 #endif 1053 1054 return HANDLER_GO_ON; 1055 } 1056 1057 URIHANDLER_FUNC(mod_extforward_uri_handler) { 1058 plugin_data *p = p_d; 1059 const buffer *forwarded = NULL; 1060 int is_forwarded_header = 0; 1061 1062 mod_extforward_patch_config(r, p); 1063 if (NULL == p->conf.forwarder) return HANDLER_GO_ON; 1064 1065 if (r->conf.log_request_handling) { 1066 log_error(r->conf.errh, __FILE__, __LINE__, 1067 "-- mod_extforward_uri_handler called"); 1068 } 1069 1070 if (p->conf.hap_PROXY_ssl_client_verify) { 1071 const data_string *ds; 1072 handler_ctx *hctx = r->con->plugin_ctx[p->id]; 1073 if (NULL != hctx && hctx->ssl_client_verify && NULL != hctx->env 1074 && NULL != (ds = (const data_string *)array_get_element_klen(hctx->env, CONST_STR_LEN("SSL_CLIENT_S_DN_CN")))) { 1075 http_header_env_set(r, 1076 CONST_STR_LEN("SSL_CLIENT_VERIFY"), 1077 CONST_STR_LEN("SUCCESS")); 1078 http_header_env_set(r, 1079 CONST_STR_LEN("REMOTE_USER"), 1080 CONST_BUF_LEN(&ds->value)); 1081 http_header_env_set(r, 1082 CONST_STR_LEN("AUTH_TYPE"), 1083 CONST_STR_LEN("SSL_CLIENT_VERIFY")); 1084 } else { 1085 http_header_env_set(r, 1086 CONST_STR_LEN("SSL_CLIENT_VERIFY"), 1087 CONST_STR_LEN("NONE")); 1088 } 1089 } 1090 1091 /* Note: headers are parsed per-request even when using HAProxy PROXY 1092 * protocol since Forwarded header might provide additional info and 1093 * internal _L_ vars might be set for later use by mod_proxy or others*/ 1094 /*if (p->conf.hap_PROXY) return HANDLER_GO_ON;*/ 1095 1096 if (NULL == p->conf.headers) return HANDLER_GO_ON; 1097 1098 /* Do not reparse headers for same request, e.g after HANDER_COMEBACK 1099 * from mod_rewrite, mod_magnet MAGNET_RESTART_REQUEST, mod_cgi 1100 * cgi.local-redir, or gw_backend reconnect. This has the implication 1101 * that mod_magnet and mod_cgi with local-redir should not modify 1102 * Forwarded or related headers and expect effects here. */ 1103 handler_ctx *hctx = r->con->plugin_ctx[p->id]; 1104 if (NULL != hctx && NULL != hctx->saved_remote_addr_buf 1105 && hctx->request_count == r->con->request_count) 1106 return HANDLER_GO_ON; 1107 1108 for (uint32_t k = 0; k < p->conf.headers->used; ++k) { 1109 const data_string * const ds = (data_string *)p->conf.headers->data[k]; 1110 const buffer * const hdr = &ds->value; 1111 forwarded = http_header_request_get(r, ds->ext, CONST_BUF_LEN(hdr)); 1112 if (forwarded) { 1113 is_forwarded_header = (ds->ext == HTTP_HEADER_FORWARDED); 1114 break; 1115 } 1116 } 1117 if (NULL == forwarded) { 1118 if (r->conf.log_request_handling) { 1119 log_error(r->conf.errh, __FILE__, __LINE__, 1120 "no forward header found, skipping"); 1121 } 1122 1123 return HANDLER_GO_ON; 1124 } 1125 1126 /* if the remote ip itself is not trusted, then do nothing */ 1127 if (!is_connection_trusted(r->con, p)) { 1128 if (r->conf.log_request_handling) { 1129 log_error(r->conf.errh, __FILE__, __LINE__, 1130 "remote address %s is NOT a trusted proxy, skipping", 1131 r->con->dst_addr_buf->ptr); 1132 } 1133 1134 return HANDLER_GO_ON; 1135 } 1136 1137 if (is_forwarded_header) { 1138 return mod_extforward_Forwarded(r, p, forwarded); 1139 } 1140 1141 return mod_extforward_X_Forwarded_For(r, p, forwarded); 1142 } 1143 1144 1145 REQUEST_FUNC(mod_extforward_handle_request_env) { 1146 plugin_data *p = p_d; 1147 handler_ctx *hctx = r->con->plugin_ctx[p->id]; 1148 if (NULL == hctx || NULL == hctx->env) return HANDLER_GO_ON; 1149 for (uint32_t i=0; i < hctx->env->used; ++i) { 1150 /* note: replaces values which may have been set by mod_openssl 1151 * (when mod_extforward is listed after mod_openssl in server.modules)*/ 1152 data_string *ds = (data_string *)hctx->env->data[i]; 1153 http_header_env_set(r, 1154 CONST_BUF_LEN(&ds->key), CONST_BUF_LEN(&ds->value)); 1155 } 1156 return HANDLER_GO_ON; 1157 } 1158 1159 1160 REQUEST_FUNC(mod_extforward_restore) { 1161 /* Preserve changed addr for lifetime of h2 connection; upstream proxy 1162 * should not reuse same h2 connection for requests from different clients*/ 1163 if (r->http_version > HTTP_VERSION_1_1) /*(e.g. HTTP_VERSION_2)*/ 1164 return HANDLER_GO_ON; 1165 /* XXX: should change this to not occur at request reset, 1166 * but instead at connection reset */ 1167 plugin_data *p = p_d; 1168 connection * const con = r->con; 1169 handler_ctx *hctx = con->plugin_ctx[p->id]; 1170 1171 if (!hctx) return HANDLER_GO_ON; 1172 1173 if (NULL != hctx->saved_remote_addr_buf) { 1174 con->dst_addr = hctx->saved_remote_addr; 1175 buffer_free(con->dst_addr_buf); 1176 con->dst_addr_buf = hctx->saved_remote_addr_buf; 1177 hctx->saved_remote_addr_buf = NULL; 1178 /* Now, clean the conf_cond cache, because we may have changed the results of tests */ 1179 config_cond_cache_reset_item(r, COMP_HTTP_REMOTE_IP); 1180 } 1181 1182 if (NULL == hctx->env) { 1183 handler_ctx_free(hctx); 1184 con->plugin_ctx[p->id] = NULL; 1185 } 1186 1187 return HANDLER_GO_ON; 1188 } 1189 1190 1191 CONNECTION_FUNC(mod_extforward_handle_con_close) 1192 { 1193 plugin_data *p = p_d; 1194 handler_ctx *hctx = con->plugin_ctx[p->id]; 1195 if (NULL != hctx) { 1196 if (NULL != hctx->saved_network_read) { 1197 con->network_read = hctx->saved_network_read; 1198 } 1199 if (NULL != hctx->saved_remote_addr_buf) { 1200 con->dst_addr = hctx->saved_remote_addr; 1201 buffer_free(con->dst_addr_buf); 1202 con->dst_addr_buf = hctx->saved_remote_addr_buf; 1203 } 1204 if (NULL != hctx->env) { 1205 array_free(hctx->env); 1206 } 1207 handler_ctx_free(hctx); 1208 con->plugin_ctx[p->id] = NULL; 1209 } 1210 1211 return HANDLER_GO_ON; 1212 } 1213 1214 1215 static int mod_extforward_network_read (connection *con, chunkqueue *cq, off_t max_bytes); 1216 1217 CONNECTION_FUNC(mod_extforward_handle_con_accept) 1218 { 1219 request_st * const r = &con->request; 1220 plugin_data *p = p_d; 1221 mod_extforward_patch_config(r, p); 1222 if (!p->conf.hap_PROXY) return HANDLER_GO_ON; 1223 if (NULL == p->conf.forwarder) return HANDLER_GO_ON; 1224 if (is_connection_trusted(con, p)) { 1225 handler_ctx *hctx = handler_ctx_init(); 1226 con->plugin_ctx[p->id] = hctx; 1227 hctx->saved_network_read = con->network_read; 1228 con->network_read = mod_extforward_network_read; 1229 } 1230 else { 1231 if (r->conf.log_request_handling) { 1232 log_error(r->conf.errh, __FILE__, __LINE__, 1233 "remote address %s is NOT a trusted proxy, skipping", 1234 con->dst_addr_buf->ptr); 1235 } 1236 } 1237 return HANDLER_GO_ON; 1238 } 1239 1240 1241 int mod_extforward_plugin_init(plugin *p); 1242 int mod_extforward_plugin_init(plugin *p) { 1243 p->version = LIGHTTPD_VERSION_ID; 1244 p->name = "extforward"; 1245 1246 p->init = mod_extforward_init; 1247 p->handle_connection_accept = mod_extforward_handle_con_accept; 1248 p->handle_uri_raw = mod_extforward_uri_handler; 1249 p->handle_request_env = mod_extforward_handle_request_env; 1250 p->handle_request_done = mod_extforward_restore; 1251 p->handle_request_reset = mod_extforward_restore; 1252 p->handle_connection_close = mod_extforward_handle_con_close; 1253 p->set_defaults = mod_extforward_set_defaults; 1254 p->cleanup = mod_extforward_free; 1255 1256 return 0; 1257 } 1258 1259 1260 1261 1262 /* Modified from: 1263 * http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt 1264 * 1265 9. Sample code 1266 1267 The code below is an example of how a receiver may deal with both versions of 1268 the protocol header for TCP over IPv4 or IPv6. The function is supposed to be 1269 called upon a read event. Addresses may be directly copied into their final 1270 memory location since they're transported in network byte order. The sending 1271 side is even simpler and can easily be deduced from this sample code. 1272 * 1273 */ 1274 1275 union hap_PROXY_hdr { 1276 struct { 1277 char line[108]; 1278 } v1; 1279 struct { 1280 uint8_t sig[12]; 1281 uint8_t ver_cmd; 1282 uint8_t fam; 1283 uint16_t len; 1284 union { 1285 struct { /* for TCP/UDP over IPv4, len = 12 */ 1286 uint32_t src_addr; 1287 uint32_t dst_addr; 1288 uint16_t src_port; 1289 uint16_t dst_port; 1290 } ip4; 1291 struct { /* for TCP/UDP over IPv6, len = 36 */ 1292 uint8_t src_addr[16]; 1293 uint8_t dst_addr[16]; 1294 uint16_t src_port; 1295 uint16_t dst_port; 1296 } ip6; 1297 struct { /* for AF_UNIX sockets, len = 216 */ 1298 uint8_t src_addr[108]; 1299 uint8_t dst_addr[108]; 1300 } unx; 1301 } addr; 1302 } v2; 1303 }; 1304 1305 /* 1306 If the length specified in the PROXY protocol header indicates that additional 1307 bytes are part of the header beyond the address information, a receiver may 1308 choose to skip over and ignore those bytes, or attempt to interpret those 1309 bytes. 1310 1311 The information in those bytes will be arranged in Type-Length-Value (TLV 1312 vectors) in the following format. The first byte is the Type of the vector. 1313 The second two bytes represent the length in bytes of the value (not included 1314 the Type and Length bytes), and following the length field is the number of 1315 bytes specified by the length. 1316 */ 1317 struct pp2_tlv { 1318 uint8_t type; 1319 uint8_t length_hi; 1320 uint8_t length_lo; 1321 /*uint8_t value[0];*//* C99 zero-length array */ 1322 }; 1323 1324 /* 1325 The following types have already been registered for the <type> field : 1326 */ 1327 1328 #define PP2_TYPE_ALPN 0x01 1329 #define PP2_TYPE_AUTHORITY 0x02 1330 #define PP2_TYPE_CRC32C 0x03 1331 #define PP2_TYPE_NOOP 0x04 1332 #define PP2_TYPE_SSL 0x20 1333 #define PP2_SUBTYPE_SSL_VERSION 0x21 1334 #define PP2_SUBTYPE_SSL_CN 0x22 1335 #define PP2_SUBTYPE_SSL_CIPHER 0x23 1336 #define PP2_SUBTYPE_SSL_SIG_ALG 0x24 1337 #define PP2_SUBTYPE_SSL_KEY_ALG 0x25 1338 #define PP2_TYPE_NETNS 0x30 1339 1340 /* 1341 For the type PP2_TYPE_SSL, the value is itselv a defined like this : 1342 */ 1343 1344 struct pp2_tlv_ssl { 1345 uint8_t client; 1346 uint32_t verify; 1347 /*struct pp2_tlv sub_tlv[0];*//* C99 zero-length array */ 1348 }; 1349 1350 /* 1351 And the <client> field is made of a bit field from the following values, 1352 indicating which element is present : 1353 */ 1354 1355 #define PP2_CLIENT_SSL 0x01 1356 #define PP2_CLIENT_CERT_CONN 0x02 1357 #define PP2_CLIENT_CERT_SESS 0x04 1358 1359 1360 1361 1362 #ifndef MSG_DONTWAIT 1363 #define MSG_DONTWAIT 0 1364 #endif 1365 #ifndef MSG_NOSIGNAL 1366 #define MSG_NOSIGNAL 0 1367 #endif 1368 1369 /* returns 0 if needs to poll, <0 upon error or >0 is protocol vers (success) */ 1370 static int hap_PROXY_recv (const int fd, union hap_PROXY_hdr * const hdr, const int family, const int so_type) 1371 { 1372 static const char v2sig[12] = 1373 "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"; 1374 1375 ssize_t ret; 1376 size_t sz; 1377 int ver; 1378 1379 do { 1380 ret = recv(fd, hdr, sizeof(*hdr), MSG_PEEK|MSG_DONTWAIT|MSG_NOSIGNAL); 1381 } while (-1 == ret && errno == EINTR); 1382 1383 if (-1 == ret) 1384 return (errno == EAGAIN 1385 #ifdef EWOULDBLOCK 1386 #if EAGAIN != EWOULDBLOCK 1387 || errno == EWOULDBLOCK 1388 #endif 1389 #endif 1390 ) ? 0 : -1; 1391 1392 if (ret >= 16 && 0 == memcmp(&hdr->v2, v2sig, 12) 1393 && (hdr->v2.ver_cmd & 0xF0) == 0x20) { 1394 ver = 2; 1395 sz = 16 + (size_t)ntohs(hdr->v2.len); 1396 if ((size_t)ret < sz) 1397 return -2; /* truncated or too large header */ 1398 1399 switch (hdr->v2.ver_cmd & 0xF) { 1400 case 0x01: break; /* PROXY command */ 1401 case 0x00: break; /* LOCAL command */ 1402 default: return -2; /* not a supported command */ 1403 } 1404 } 1405 else if (ret >= 8 && 0 == memcmp(hdr->v1.line, "PROXY", 5)) { 1406 const char *end = memchr(hdr->v1.line, '\r', ret - 1); 1407 if (!end || end[1] != '\n') 1408 return -2; /* partial or invalid header */ 1409 ver = 1; 1410 sz = (size_t)(end + 2 - hdr->v1.line); /* skip header + CRLF */ 1411 } 1412 else { 1413 /* Wrong protocol */ 1414 return -2; 1415 } 1416 1417 /* we need to consume the appropriate amount of data from the socket 1418 * (overwrites existing contents of hdr with same data) */ 1419 UNUSED(family); 1420 UNUSED(so_type); 1421 do { 1422 #if defined(MSG_TRUNC) && defined(__linux__) 1423 if ((family==AF_INET || family==AF_INET6) && so_type == SOCK_STREAM) { 1424 ret = recv(fd, hdr, sz, MSG_TRUNC|MSG_DONTWAIT|MSG_NOSIGNAL); 1425 if (ret >= 0 || errno != EINVAL) continue; 1426 } 1427 #endif 1428 ret = recv(fd, hdr, sz, MSG_DONTWAIT|MSG_NOSIGNAL); 1429 } while (-1 == ret && errno == EINTR); 1430 if (ret < 0) return -1; 1431 if (ret != (ssize_t)sz) { 1432 errno = EIO; /*(partial read; valid but unexpected; not handled)*/ 1433 return -1; 1434 } 1435 if (1 == ver) hdr->v1.line[sz-2] = '\0'; /*terminate str to ease parsing*/ 1436 return ver; 1437 } 1438 1439 1440 __attribute_pure__ 1441 static int mod_extforward_str_to_port (const char * const s) 1442 { 1443 /*(more strict than strtol(); digits only)*/ 1444 int port = 0; 1445 for (int i = 0; i < 5; ++i, port *= 10) { 1446 if (!light_isdigit(s[i])) return -1; 1447 port += (s[i] - '0'); 1448 if (s[i+1] == '\0') return port; 1449 } 1450 return -1; 1451 } 1452 1453 /* coverity[-tainted_data_sink: arg-1] */ 1454 static int mod_extforward_hap_PROXY_v1 (connection * const con, 1455 union hap_PROXY_hdr * const hdr) 1456 { 1457 #ifdef __COVERITY__ 1458 __coverity_tainted_data_sink__(hdr); 1459 #endif 1460 1461 /* samples 1462 * "PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535\r\n" 1463 * "PROXY TCP6 ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n" 1464 * "PROXY UNKNOWN\r\n" 1465 * "PROXY UNKNOWN ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n" 1466 */ 1467 char *s = hdr->v1.line + sizeof("PROXY")-1; /*checked in hap_PROXY_recv()*/ 1468 char *src_addr, *dst_addr, *src_port, *dst_port; 1469 int family; 1470 int src_lport, dst_lport; 1471 if (*s != ' ') return -1; 1472 ++s; 1473 if (s[0] == 'T' && s[1] == 'C' && s[2] == 'P' && s[4] == ' ') { 1474 if (s[3] == '4') { 1475 family = AF_INET; 1476 } else if (s[3] == '6') { 1477 family = AF_INET6; 1478 } 1479 else { 1480 return -1; 1481 } 1482 s += 5; 1483 } 1484 else if (0 == memcmp(s, "UNKNOWN", sizeof("UNKNOWN")-1) 1485 && (s[7] == '\0' || s[7] == ' ')) { 1486 return 0; /* keep local connection address */ 1487 } 1488 else { 1489 return -1; 1490 } 1491 1492 /*(strsep() should be fairly portable, but is not standard)*/ 1493 src_addr = s; 1494 dst_addr = strchr(src_addr, ' '); 1495 if (NULL == dst_addr) return -1; 1496 *dst_addr++ = '\0'; 1497 src_port = strchr(dst_addr, ' '); 1498 if (NULL == src_port) return -1; 1499 *src_port++ = '\0'; 1500 dst_port = strchr(src_port, ' '); 1501 if (NULL == dst_port) return -1; 1502 *dst_port++ = '\0'; 1503 1504 src_lport = mod_extforward_str_to_port(src_port); 1505 if (src_lport <= 0) return -1; 1506 dst_lport = mod_extforward_str_to_port(dst_port); 1507 if (dst_lport <= 0) return -1; 1508 1509 if (1 != sock_addr_inet_pton(&con->dst_addr, 1510 src_addr, family, (unsigned short)src_lport)) 1511 return -1; 1512 /* Forwarded by=... could be saved here. 1513 * (see additional comments in mod_extforward_hap_PROXY_v2()) */ 1514 1515 /* re-parse addr to string to normalize 1516 * (instead of trusting PROXY to provide canonicalized src_addr string) 1517 * (should prefer PROXY v2 protocol if concerned about performance) */ 1518 sock_addr_inet_ntop_copy_buffer(con->dst_addr_buf, &con->dst_addr); 1519 1520 return 0; 1521 } 1522 1523 1524 /* coverity[-tainted_data_sink: arg-1] */ 1525 static int mod_extforward_hap_PROXY_v2 (connection * const con, 1526 union hap_PROXY_hdr * const hdr) 1527 { 1528 #ifdef __COVERITY__ 1529 __coverity_tainted_data_sink__(hdr); 1530 #endif 1531 1532 /* If HAProxy-PROXY protocol used, then lighttpd acts as transparent proxy, 1533 * masquerading as servicing the client IP provided in by HAProxy-PROXY hdr. 1534 * The connecting con->dst_addr and con->dst_addr_buf are not saved here, 1535 * so that info is lost unless getsockname() and getpeername() are used. 1536 * One result is that mod_proxy will use the masqueraded IP instead of the 1537 * actual IP when updated Forwarded and X-Forwarded-For (but if actual 1538 * connection IPs needed, better to save the info here rather than use 1539 * syscalls to retrieve the info later). 1540 * (Exception: con->dst_addr can be further changed if mod_extforward parses 1541 * Forwarded or X-Forwarded-For request headers later, after request headers 1542 * have been received.) 1543 */ 1544 1545 /* Forwarded by=... could be saved here. The by param is for backends to be 1546 * able to construct URIs for that interface (interface on server which 1547 * received request and made PROXY connection here), though that server 1548 * should provide that information in updated Forwarded or X-Forwarded-For 1549 * HTTP headers */ 1550 /*struct sockaddr_storage by;*/ 1551 1552 /* Addresses provided by HAProxy-PROXY protocol are in network byte order. 1553 * Note: addr info is not validated, so do not accept HAProxy-PROXY 1554 * protocol from untrusted servers. For example, untrusted servers from 1555 * which HAProxy-PROXY protocol is accepted (don't do that) could pretend 1556 * to be from the internal network and might thereby bypass security policy. 1557 */ 1558 1559 /* (Clear con->dst_addr with memset() in case actual and proxies IPs 1560 * are different domains, e.g. one is IPv4 and the other is IPv6) */ 1561 1562 struct pp2_tlv *tlv; 1563 uint32_t sz = ntohs(hdr->v2.len); 1564 uint32_t len = 0; 1565 1566 switch (hdr->v2.ver_cmd & 0xF) { 1567 case 0x01: break; /* PROXY command */ 1568 case 0x00: return 0;/* LOCAL command; keep local connection address */ 1569 default: return -1;/* should not happen; validated in hap_PROXY_recv()*/ 1570 } 1571 1572 /* PROXY command */ 1573 1574 switch (hdr->v2.fam) { 1575 case 0x11: /* TCPv4 */ 1576 sock_addr_assign(&con->dst_addr, AF_INET, hdr->v2.addr.ip4.src_port, 1577 &hdr->v2.addr.ip4.src_addr); 1578 sock_addr_inet_ntop_copy_buffer(con->dst_addr_buf, &con->dst_addr); 1579 #if 0 1580 ((struct sockaddr_in *)&by)->sin_family = AF_INET; 1581 ((struct sockaddr_in *)&by)->sin_addr.s_addr = 1582 hdr->v2.addr.ip4.dst_addr; 1583 ((struct sockaddr_in *)&by)->sin_port = 1584 hdr->v2.addr.ip4.dst_port; 1585 #endif 1586 len = (uint32_t)sizeof(hdr->v2.addr.ip4); 1587 break; 1588 #ifdef HAVE_IPV6 1589 case 0x21: /* TCPv6 */ 1590 sock_addr_assign(&con->dst_addr, AF_INET6, hdr->v2.addr.ip6.src_port, 1591 &hdr->v2.addr.ip6.src_addr); 1592 sock_addr_inet_ntop_copy_buffer(con->dst_addr_buf, &con->dst_addr); 1593 #if 0 1594 ((struct sockaddr_in6 *)&by)->sin6_family = AF_INET6; 1595 memcpy(&((struct sockaddr_in6 *)&by)->sin6_addr, 1596 hdr->v2.addr.ip6.dst_addr, 16); 1597 ((struct sockaddr_in6 *)&by)->sin6_port = 1598 hdr->v2.addr.ip6.dst_port; 1599 #endif 1600 len = (uint32_t)sizeof(hdr->v2.addr.ip6); 1601 break; 1602 #endif 1603 #ifdef HAVE_SYS_UN_H 1604 case 0x31: /* UNIX domain socket */ 1605 { 1606 char *src_addr = (char *)hdr->v2.addr.unx.src_addr; 1607 char *z = memchr(src_addr, '\0', UNIX_PATH_MAX); 1608 if (NULL == z) return -1; /* invalid addr; too long */ 1609 len = (uint32_t)(z - src_addr + 1); /*(+1 for '\0')*/ 1610 sock_addr_assign(&con->dst_addr, AF_UNIX, 0, src_addr); 1611 buffer_copy_string_len(con->dst_addr_buf, src_addr, len); 1612 } 1613 #if 0 /*(dst_addr should be identical to src_addr for AF_UNIX)*/ 1614 ((struct sockaddr_un *)&by)->sun_family = AF_UNIX; 1615 memcpy(&((struct sockaddr_un *)&by)->sun_path, 1616 hdr->v2.addr.unx.dst_addr, 108); 1617 #endif 1618 len = (uint32_t)sizeof(hdr->v2.addr.unx); 1619 break; 1620 #endif 1621 default: /* keep local connection address; unsupported protocol */ 1622 return 0; 1623 } 1624 1625 /* (optional) Type-Length-Value (TLV vectors) follow addresses */ 1626 1627 tlv = (struct pp2_tlv *)((char *)hdr + 16); 1628 for (sz -= len, len -= 3; sz >= 3; sz -= 3 + len) { 1629 tlv = (struct pp2_tlv *)((char *)tlv + 3 + len); 1630 len = ((uint32_t)tlv->length_hi << 8) | tlv->length_lo; 1631 if (3 + len > sz) break; /*(invalid TLV)*/ 1632 switch (tlv->type) { 1633 #if 0 /*(not implemented here)*/ 1634 case PP2_TYPE_ALPN: 1635 case PP2_TYPE_AUTHORITY: 1636 case PP2_TYPE_CRC32C: 1637 #endif 1638 case PP2_TYPE_SSL: { 1639 static const uint32_t zero = 0; 1640 handler_ctx *hctx = 1641 con->plugin_ctx[mod_extforward_plugin_data_singleton->id]; 1642 struct pp2_tlv_ssl *tlv_ssl = 1643 (struct pp2_tlv_ssl *)(void *)((char *)tlv+3); 1644 struct pp2_tlv *subtlv = tlv; 1645 if (tlv_ssl->client & PP2_CLIENT_SSL) { 1646 con->proto_default_port = 443; /* "https" */ 1647 } 1648 if ((tlv_ssl->client & (PP2_CLIENT_CERT_CONN|PP2_CLIENT_CERT_SESS)) 1649 && 0 == memcmp(&tlv_ssl->verify, &zero, 4)) { /* misaligned */ 1650 hctx->ssl_client_verify = 1; 1651 } 1652 for (uint32_t subsz = len-5, n = 5; subsz >= 3; subsz -= 3 + n) { 1653 subtlv = (struct pp2_tlv *)((char *)subtlv + 3 + n); 1654 n = ((uint32_t)subtlv->length_hi << 8) | subtlv->length_lo; 1655 if (3 + n > subsz) break; /*(invalid TLV)*/ 1656 if (NULL == hctx->env) hctx->env = array_init(8); 1657 switch (subtlv->type) { 1658 case PP2_SUBTYPE_SSL_VERSION: 1659 array_set_key_value(hctx->env, 1660 CONST_STR_LEN("SSL_PROTOCOL"), 1661 (char *)subtlv+3, n); 1662 break; 1663 case PP2_SUBTYPE_SSL_CN: 1664 /* (tlv_ssl->client & PP2_CLIENT_CERT_CONN) 1665 * or 1666 * (tlv_ssl->client & PP2_CLIENT_CERT_SESS) */ 1667 array_set_key_value(hctx->env, 1668 CONST_STR_LEN("SSL_CLIENT_S_DN_CN"), 1669 (char *)subtlv+3, n); 1670 break; 1671 case PP2_SUBTYPE_SSL_CIPHER: 1672 array_set_key_value(hctx->env, 1673 CONST_STR_LEN("SSL_CIPHER"), 1674 (char *)subtlv+3, n); 1675 break; 1676 case PP2_SUBTYPE_SSL_SIG_ALG: 1677 array_set_key_value(hctx->env, 1678 CONST_STR_LEN("SSL_SERVER_A_SIG"), 1679 (char *)subtlv+3, n); 1680 break; 1681 case PP2_SUBTYPE_SSL_KEY_ALG: 1682 array_set_key_value(hctx->env, 1683 CONST_STR_LEN("SSL_SERVER_A_KEY"), 1684 (char *)subtlv+3, n); 1685 break; 1686 default: 1687 break; 1688 } 1689 } 1690 break; 1691 } 1692 #if 0 /*(not implemented here)*/ 1693 case PP2_TYPE_NETNS: 1694 #endif 1695 /*case PP2_TYPE_NOOP:*//* no-op */ 1696 default: 1697 break; 1698 } 1699 } 1700 1701 return 0; 1702 } 1703 1704 1705 static int mod_extforward_network_read (connection *con, 1706 chunkqueue *cq, off_t max_bytes) 1707 { 1708 /* XXX: when using hap-PROXY protocol, currently avoid overhead of setting 1709 * _L_ environment variables for mod_proxy to accurately set Forwarded hdr 1710 * In the future, might add config switch to enable doing this extra work */ 1711 1712 union hap_PROXY_hdr hdr; 1713 log_error_st *errh; 1714 const int family = sock_addr_get_family(&con->dst_addr); 1715 int rc = hap_PROXY_recv(con->fd, &hdr, family, SOCK_STREAM); 1716 switch (rc) { 1717 case 2: rc = mod_extforward_hap_PROXY_v2(con, &hdr); break; 1718 case 1: rc = mod_extforward_hap_PROXY_v1(con, &hdr); break; 1719 case 0: return 0; /*(errno == EAGAIN || errno == EWOULDBLOCK)*/ 1720 case -1: errh = con->srv->errh; 1721 log_perror(errh,__FILE__,__LINE__,"hap-PROXY recv()"); 1722 rc = -1; break; 1723 case -2: errh = con->srv->errh; 1724 log_error(errh,__FILE__,__LINE__, 1725 "hap-PROXY proto received invalid/unsupported request"); 1726 __attribute_fallthrough__ 1727 default: rc = -1; break; 1728 } 1729 1730 handler_ctx *hctx = 1731 con->plugin_ctx[mod_extforward_plugin_data_singleton->id]; 1732 con->network_read = hctx->saved_network_read; 1733 hctx->saved_network_read = NULL; 1734 return (0 == rc) ? con->network_read(con, cq, max_bytes) : rc; 1735 } 1736