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