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