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