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