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