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