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