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