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