xref: /lighttpd1.4/src/mod_proxy.c (revision 5e14db43)
18abd06a7SGlenn Strauss #include "first.h"
28abd06a7SGlenn Strauss 
322e8b456SStefan Bühler #include <string.h>
422e8b456SStefan Bühler #include <stdlib.h>
5bcdc6a3bSJan Kneschke 
645b970e6SGlenn Strauss #include "gw_backend.h"
745b970e6SGlenn Strauss #include "base.h"
845b970e6SGlenn Strauss #include "array.h"
945b970e6SGlenn Strauss #include "buffer.h"
106d62a498SGlenn Strauss #include "fdevent.h"
11c56b2108SGlenn Strauss #include "http_kv.h"
123dd3cde9SGlenn Strauss #include "http_header.h"
1345b970e6SGlenn Strauss #include "log.h"
141367f606SGlenn Strauss #include "sock_addr.h"
15bcdc6a3bSJan Kneschke 
16bcdc6a3bSJan Kneschke /**
17bcdc6a3bSJan Kneschke  *
180a635fc8SGlenn Strauss  * HTTP reverse proxy
19bcdc6a3bSJan Kneschke  *
200a635fc8SGlenn Strauss  * TODO:      - HTTP/1.1
210a635fc8SGlenn Strauss  *            - HTTP/1.1 persistent connection with upstream servers
22bcdc6a3bSJan Kneschke  */
230a635fc8SGlenn Strauss 
24036d3d3dSGlenn Strauss /* (future: might split struct and move part to http-header-glue.c) */
25036d3d3dSGlenn Strauss typedef struct http_header_remap_opts {
26036d3d3dSGlenn Strauss     const array *urlpaths;
27036d3d3dSGlenn Strauss     const array *hosts_request;
28036d3d3dSGlenn Strauss     const array *hosts_response;
29fe5740d5SGlenn Strauss     int force_http10;
30036d3d3dSGlenn Strauss     int https_remap;
3114656f8fSGlenn Strauss     int upgrade;
323770df23SGlenn Strauss     int connect_method;
33036d3d3dSGlenn Strauss     /*(not used in plugin_config, but used in handler_ctx)*/
34036d3d3dSGlenn Strauss     const buffer *http_host;
35036d3d3dSGlenn Strauss     const buffer *forwarded_host;
36036d3d3dSGlenn Strauss     const data_string *forwarded_urlpath;
37036d3d3dSGlenn Strauss } http_header_remap_opts;
38036d3d3dSGlenn Strauss 
39b5e848d5SJan Kneschke typedef enum {
40b2e2d42cSGlenn Strauss 	PROXY_FORWARDED_NONE         = 0x00,
41b2e2d42cSGlenn Strauss 	PROXY_FORWARDED_FOR          = 0x01,
42b2e2d42cSGlenn Strauss 	PROXY_FORWARDED_PROTO        = 0x02,
43b2e2d42cSGlenn Strauss 	PROXY_FORWARDED_HOST         = 0x04,
44b2e2d42cSGlenn Strauss 	PROXY_FORWARDED_BY           = 0x08,
45b2e2d42cSGlenn Strauss 	PROXY_FORWARDED_REMOTE_USER  = 0x10
46b2e2d42cSGlenn Strauss } proxy_forwarded_t;
47b2e2d42cSGlenn Strauss 
48bcdc6a3bSJan Kneschke typedef struct {
494a6fe838SGlenn Strauss     gw_plugin_config gw; /* start must match layout of gw_plugin_config */
504a6fe838SGlenn Strauss     unsigned int replace_http_host;
51b2e2d42cSGlenn Strauss     unsigned int forwarded;
52036d3d3dSGlenn Strauss     http_header_remap_opts header;
53bcdc6a3bSJan Kneschke } plugin_config;
54bcdc6a3bSJan Kneschke 
55bcdc6a3bSJan Kneschke typedef struct {
56bcdc6a3bSJan Kneschke     PLUGIN_DATA;
574a6fe838SGlenn Strauss     pid_t srv_pid; /* must match layout of gw_plugin_data through conf member */
58bcdc6a3bSJan Kneschke     plugin_config conf;
594a6fe838SGlenn Strauss     plugin_config defaults;
60bcdc6a3bSJan Kneschke } plugin_data;
61bcdc6a3bSJan Kneschke 
62b2e2d42cSGlenn Strauss static int proxy_check_extforward;
63b2e2d42cSGlenn Strauss 
64bcdc6a3bSJan Kneschke typedef struct {
6545b970e6SGlenn Strauss 	gw_handler_ctx gw;
66dc91e406SGlenn Strauss 	plugin_config conf;
67bcdc6a3bSJan Kneschke } handler_ctx;
68bcdc6a3bSJan Kneschke 
69bcdc6a3bSJan Kneschke 
INIT_FUNC(mod_proxy_init)70bcdc6a3bSJan Kneschke INIT_FUNC(mod_proxy_init) {
71*5e14db43SGlenn Strauss     return ck_calloc(1, sizeof(plugin_data));
724a6fe838SGlenn Strauss }
73bcdc6a3bSJan Kneschke 
74bcdc6a3bSJan Kneschke 
mod_proxy_free_config(plugin_data * const p)754a6fe838SGlenn Strauss static void mod_proxy_free_config(plugin_data * const p)
764a6fe838SGlenn Strauss {
774a6fe838SGlenn Strauss     if (NULL == p->cvlist) return;
784a6fe838SGlenn Strauss     /* (init i to 0 if global context; to 1 to skip empty global context) */
794a6fe838SGlenn Strauss     for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) {
804a6fe838SGlenn Strauss         config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
814a6fe838SGlenn Strauss         for (; -1 != cpv->k_id; ++cpv) {
824a6fe838SGlenn Strauss             switch (cpv->k_id) {
834a6fe838SGlenn Strauss               case 5: /* proxy.header */
844a6fe838SGlenn Strauss                 if (cpv->vtype == T_CONFIG_LOCAL) free(cpv->v.v);
854a6fe838SGlenn Strauss                 break;
864a6fe838SGlenn Strauss               default:
874a6fe838SGlenn Strauss                 break;
884a6fe838SGlenn Strauss             }
894a6fe838SGlenn Strauss         }
904a6fe838SGlenn Strauss     }
91bcdc6a3bSJan Kneschke }
92bcdc6a3bSJan Kneschke 
93bcdc6a3bSJan Kneschke 
FREE_FUNC(mod_proxy_free)94bcdc6a3bSJan Kneschke FREE_FUNC(mod_proxy_free) {
95b73949e0SGlenn Strauss     plugin_data * const p = p_d;
964a6fe838SGlenn Strauss     mod_proxy_free_config(p);
97b73949e0SGlenn Strauss     gw_free(p);
98bcdc6a3bSJan Kneschke }
99bcdc6a3bSJan Kneschke 
mod_proxy_merge_config_cpv(plugin_config * const pconf,const config_plugin_value_t * const cpv)1004a6fe838SGlenn Strauss static void mod_proxy_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv)
1014a6fe838SGlenn Strauss {
1024a6fe838SGlenn Strauss     switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */
1034a6fe838SGlenn Strauss       case 0: /* proxy.server */
1044a6fe838SGlenn Strauss         if (cpv->vtype == T_CONFIG_LOCAL) {
1054a6fe838SGlenn Strauss             gw_plugin_config * const gw = cpv->v.v;
1064a6fe838SGlenn Strauss             pconf->gw.exts      = gw->exts;
1074a6fe838SGlenn Strauss             pconf->gw.exts_auth = gw->exts_auth;
1084a6fe838SGlenn Strauss             pconf->gw.exts_resp = gw->exts_resp;
1094a6fe838SGlenn Strauss         }
1104a6fe838SGlenn Strauss         break;
1114a6fe838SGlenn Strauss       case 1: /* proxy.balance */
1124a6fe838SGlenn Strauss         /*if (cpv->vtype == T_CONFIG_LOCAL)*//*always true here for this param*/
1134a6fe838SGlenn Strauss             pconf->gw.balance = (int)cpv->v.u;
1144a6fe838SGlenn Strauss         break;
1154a6fe838SGlenn Strauss       case 2: /* proxy.debug */
1164a6fe838SGlenn Strauss         pconf->gw.debug = (int)cpv->v.u;
1174a6fe838SGlenn Strauss         break;
1184a6fe838SGlenn Strauss       case 3: /* proxy.map-extensions */
1194a6fe838SGlenn Strauss         pconf->gw.ext_mapping = cpv->v.a;
1204a6fe838SGlenn Strauss         break;
1214a6fe838SGlenn Strauss       case 4: /* proxy.forwarded */
1224a6fe838SGlenn Strauss         /*if (cpv->vtype == T_CONFIG_LOCAL)*//*always true here for this param*/
1234a6fe838SGlenn Strauss             pconf->forwarded = cpv->v.u;
1244a6fe838SGlenn Strauss         break;
1254a6fe838SGlenn Strauss       case 5: /* proxy.header */
1264a6fe838SGlenn Strauss         /*if (cpv->vtype == T_CONFIG_LOCAL)*//*always true here for this param*/
1274a6fe838SGlenn Strauss         pconf->header = *(http_header_remap_opts *)cpv->v.v; /*(copies struct)*/
1284a6fe838SGlenn Strauss         break;
1294a6fe838SGlenn Strauss       case 6: /* proxy.replace-http-host */
1304a6fe838SGlenn Strauss         pconf->replace_http_host = cpv->v.u;
1314a6fe838SGlenn Strauss         break;
1324a6fe838SGlenn Strauss       default:/* should not happen */
1334a6fe838SGlenn Strauss         return;
1344a6fe838SGlenn Strauss     }
135bcdc6a3bSJan Kneschke }
136bcdc6a3bSJan Kneschke 
137bcdc6a3bSJan Kneschke 
mod_proxy_merge_config(plugin_config * const pconf,const config_plugin_value_t * cpv)1384a6fe838SGlenn Strauss static void mod_proxy_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv)
1394a6fe838SGlenn Strauss {
1404a6fe838SGlenn Strauss     do {
1414a6fe838SGlenn Strauss         mod_proxy_merge_config_cpv(pconf, cpv);
1424a6fe838SGlenn Strauss     } while ((++cpv)->k_id != -1);
1434a6fe838SGlenn Strauss }
1444a6fe838SGlenn Strauss 
1454a6fe838SGlenn Strauss 
mod_proxy_patch_config(request_st * const r,plugin_data * const p)1467c7f8c46SGlenn Strauss static void mod_proxy_patch_config(request_st * const r, plugin_data * const p)
1474a6fe838SGlenn Strauss {
1484a6fe838SGlenn Strauss     memcpy(&p->conf, &p->defaults, sizeof(plugin_config));
1494a6fe838SGlenn Strauss     for (int i = 1, used = p->nconfig; i < used; ++i) {
1507c7f8c46SGlenn Strauss         if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id))
1514a6fe838SGlenn Strauss             mod_proxy_merge_config(&p->conf, p->cvlist+p->cvlist[i].v.u2[0]);
1524a6fe838SGlenn Strauss     }
1534a6fe838SGlenn Strauss }
1544a6fe838SGlenn Strauss 
1554a6fe838SGlenn Strauss 
mod_proxy_parse_forwarded(server * srv,const array * a)1564a6fe838SGlenn Strauss static unsigned int mod_proxy_parse_forwarded(server *srv, const array *a)
1574a6fe838SGlenn Strauss {
1584a6fe838SGlenn Strauss     unsigned int forwarded = 0;
1594a6fe838SGlenn Strauss     for (uint32_t j = 0, used = a->used; j < used; ++j) {
1604a6fe838SGlenn Strauss         proxy_forwarded_t param;
1614a6fe838SGlenn Strauss         data_unset *du = a->data[j];
1624a6fe838SGlenn Strauss         if (buffer_eq_slen(&du->key, CONST_STR_LEN("by")))
1634a6fe838SGlenn Strauss             param = PROXY_FORWARDED_BY;
1644a6fe838SGlenn Strauss         else if (buffer_eq_slen(&du->key, CONST_STR_LEN("for")))
1654a6fe838SGlenn Strauss             param = PROXY_FORWARDED_FOR;
1664a6fe838SGlenn Strauss         else if (buffer_eq_slen(&du->key, CONST_STR_LEN("host")))
1674a6fe838SGlenn Strauss             param = PROXY_FORWARDED_HOST;
1684a6fe838SGlenn Strauss         else if (buffer_eq_slen(&du->key, CONST_STR_LEN("proto")))
1694a6fe838SGlenn Strauss             param = PROXY_FORWARDED_PROTO;
1704a6fe838SGlenn Strauss         else if (buffer_eq_slen(&du->key, CONST_STR_LEN("remote_user")))
1714a6fe838SGlenn Strauss             param = PROXY_FORWARDED_REMOTE_USER;
1724a6fe838SGlenn Strauss         else {
1734a6fe838SGlenn Strauss             log_error(srv->errh, __FILE__, __LINE__,
1744a6fe838SGlenn Strauss               "proxy.forwarded keys must be one of: "
1754a6fe838SGlenn Strauss               "by, for, host, proto, remote_user, but not: %s", du->key.ptr);
1764a6fe838SGlenn Strauss             return UINT_MAX;
1774a6fe838SGlenn Strauss         }
178730c932eSGlenn Strauss         int val = config_plugin_value_tobool(du, 2);
179730c932eSGlenn Strauss         if (2 == val) {
180730c932eSGlenn Strauss             log_error(srv->errh, __FILE__, __LINE__,
181730c932eSGlenn Strauss               "proxy.forwarded values must be one of: "
182730c932eSGlenn Strauss               "0, 1, enable, disable; error for key: %s", du->key.ptr);
183730c932eSGlenn Strauss             return UINT_MAX;
184730c932eSGlenn Strauss         }
185730c932eSGlenn Strauss         if (val)
1864a6fe838SGlenn Strauss             forwarded |= param;
1874a6fe838SGlenn Strauss     }
1884a6fe838SGlenn Strauss     return forwarded;
1894a6fe838SGlenn Strauss }
1904a6fe838SGlenn Strauss 
1914a6fe838SGlenn Strauss 
mod_proxy_parse_header_opts(server * srv,const array * a)1924a6fe838SGlenn Strauss static http_header_remap_opts * mod_proxy_parse_header_opts(server *srv, const array *a)
1934a6fe838SGlenn Strauss {
1944a6fe838SGlenn Strauss     http_header_remap_opts header;
1954a6fe838SGlenn Strauss     memset(&header, 0, sizeof(header));
1964a6fe838SGlenn Strauss     for (uint32_t j = 0, used = a->used; j < used; ++j) {
1974a6fe838SGlenn Strauss         data_array *da = (data_array *)a->data[j];
1984a6fe838SGlenn Strauss         if (buffer_eq_slen(&da->key, CONST_STR_LEN("https-remap"))) {
199730c932eSGlenn Strauss             int val = config_plugin_value_tobool((data_unset *)da, 2);
200730c932eSGlenn Strauss             if (2 == val) {
2014a6fe838SGlenn Strauss                 log_error(srv->errh, __FILE__, __LINE__,
2024a6fe838SGlenn Strauss                   "unexpected value for proxy.header; "
203730c932eSGlenn Strauss                   "expected \"https-remap\" => \"enable\" or \"disable\"");
2044a6fe838SGlenn Strauss                 return NULL;
2054a6fe838SGlenn Strauss             }
206730c932eSGlenn Strauss             header.https_remap = val;
2074a6fe838SGlenn Strauss             continue;
2084a6fe838SGlenn Strauss         }
209fe5740d5SGlenn Strauss         else if (buffer_eq_slen(&da->key, CONST_STR_LEN("force-http10"))) {
210fe5740d5SGlenn Strauss             int val = config_plugin_value_tobool((data_unset *)da, 2);
211fe5740d5SGlenn Strauss             if (2 == val) {
212fe5740d5SGlenn Strauss                 log_error(srv->errh, __FILE__, __LINE__,
213fe5740d5SGlenn Strauss                   "unexpected value for proxy.header; "
214fe5740d5SGlenn Strauss                   "expected \"force-http10\" => \"enable\" or \"disable\"");
215fe5740d5SGlenn Strauss                 return NULL;
216fe5740d5SGlenn Strauss             }
217fe5740d5SGlenn Strauss             header.force_http10 = val;
218fe5740d5SGlenn Strauss             continue;
219fe5740d5SGlenn Strauss         }
2204a6fe838SGlenn Strauss         else if (buffer_eq_slen(&da->key, CONST_STR_LEN("upgrade"))) {
221730c932eSGlenn Strauss             int val = config_plugin_value_tobool((data_unset *)da, 2);
222730c932eSGlenn Strauss             if (2 == val) {
2234a6fe838SGlenn Strauss                 log_error(srv->errh, __FILE__, __LINE__,
2244a6fe838SGlenn Strauss                   "unexpected value for proxy.header; "
2254a6fe838SGlenn Strauss                   "expected \"upgrade\" => \"enable\" or \"disable\"");
2264a6fe838SGlenn Strauss                 return NULL;
2274a6fe838SGlenn Strauss             }
228730c932eSGlenn Strauss             header.upgrade = val;
2294a6fe838SGlenn Strauss             continue;
2304a6fe838SGlenn Strauss         }
2314a6fe838SGlenn Strauss         else if (buffer_eq_slen(&da->key, CONST_STR_LEN("connect"))) {
232730c932eSGlenn Strauss             int val = config_plugin_value_tobool((data_unset *)da, 2);
233730c932eSGlenn Strauss             if (2 == val) {
2344a6fe838SGlenn Strauss                 log_error(srv->errh, __FILE__, __LINE__,
2354a6fe838SGlenn Strauss                   "unexpected value for proxy.header; "
2364a6fe838SGlenn Strauss                   "expected \"connect\" => \"enable\" or \"disable\"");
2374a6fe838SGlenn Strauss                 return NULL;
2384a6fe838SGlenn Strauss             }
239730c932eSGlenn Strauss             header.connect_method = val;
2404a6fe838SGlenn Strauss             continue;
2414a6fe838SGlenn Strauss         }
2424a6fe838SGlenn Strauss         if (da->type != TYPE_ARRAY || !array_is_kvstring(&da->value)) {
2434a6fe838SGlenn Strauss             log_error(srv->errh, __FILE__, __LINE__,
2444a6fe838SGlenn Strauss               "unexpected value for proxy.header; "
2454a6fe838SGlenn Strauss               "expected ( \"param\" => ( \"key\" => \"value\" ) ) near key %s",
2464a6fe838SGlenn Strauss               da->key.ptr);
2474a6fe838SGlenn Strauss             return NULL;
2484a6fe838SGlenn Strauss         }
2494a6fe838SGlenn Strauss         if (buffer_eq_slen(&da->key, CONST_STR_LEN("map-urlpath"))) {
2504a6fe838SGlenn Strauss             header.urlpaths = &da->value;
2514a6fe838SGlenn Strauss         }
2524a6fe838SGlenn Strauss         else if (buffer_eq_slen(&da->key, CONST_STR_LEN("map-host-request"))) {
2534a6fe838SGlenn Strauss             header.hosts_request = &da->value;
2544a6fe838SGlenn Strauss         }
2554a6fe838SGlenn Strauss         else if (buffer_eq_slen(&da->key, CONST_STR_LEN("map-host-response"))) {
2564a6fe838SGlenn Strauss             header.hosts_response = &da->value;
2574a6fe838SGlenn Strauss         }
2584a6fe838SGlenn Strauss         else {
2594a6fe838SGlenn Strauss             log_error(srv->errh, __FILE__, __LINE__,
2604a6fe838SGlenn Strauss               "unexpected key for proxy.header; "
2614a6fe838SGlenn Strauss               "expected ( \"param\" => ( \"key\" => \"value\" ) ) near key %s",
2624a6fe838SGlenn Strauss               da->key.ptr);
2634a6fe838SGlenn Strauss             return NULL;
2644a6fe838SGlenn Strauss         }
2654a6fe838SGlenn Strauss     }
2664a6fe838SGlenn Strauss 
267*5e14db43SGlenn Strauss     http_header_remap_opts *opts = ck_malloc(sizeof(header));
2684a6fe838SGlenn Strauss     memcpy(opts, &header, sizeof(header));
2694a6fe838SGlenn Strauss     return opts;
2704a6fe838SGlenn Strauss }
2714a6fe838SGlenn Strauss 
2724a6fe838SGlenn Strauss 
SETDEFAULTS_FUNC(mod_proxy_set_defaults)2734a6fe838SGlenn Strauss SETDEFAULTS_FUNC(mod_proxy_set_defaults)
2744a6fe838SGlenn Strauss {
2754a6fe838SGlenn Strauss     static const config_plugin_keys_t cpk[] = {
2764a6fe838SGlenn Strauss       { CONST_STR_LEN("proxy.server"),
27703b4c993SGlenn Strauss         T_CONFIG_ARRAY_KVARRAY,
2784a6fe838SGlenn Strauss         T_CONFIG_SCOPE_CONNECTION }
2794a6fe838SGlenn Strauss      ,{ CONST_STR_LEN("proxy.balance"),
2804a6fe838SGlenn Strauss         T_CONFIG_STRING,
2814a6fe838SGlenn Strauss         T_CONFIG_SCOPE_CONNECTION }
2824a6fe838SGlenn Strauss      ,{ CONST_STR_LEN("proxy.debug"),
2834a6fe838SGlenn Strauss         T_CONFIG_INT,
2844a6fe838SGlenn Strauss         T_CONFIG_SCOPE_CONNECTION }
2854a6fe838SGlenn Strauss      ,{ CONST_STR_LEN("proxy.map-extensions"),
28603b4c993SGlenn Strauss         T_CONFIG_ARRAY_KVSTRING,
2874a6fe838SGlenn Strauss         T_CONFIG_SCOPE_CONNECTION }
2884a6fe838SGlenn Strauss      ,{ CONST_STR_LEN("proxy.forwarded"),
28903b4c993SGlenn Strauss         T_CONFIG_ARRAY_KVANY,
2904a6fe838SGlenn Strauss         T_CONFIG_SCOPE_CONNECTION }
2914a6fe838SGlenn Strauss      ,{ CONST_STR_LEN("proxy.header"),
29203b4c993SGlenn Strauss         T_CONFIG_ARRAY_KVANY,
2934a6fe838SGlenn Strauss         T_CONFIG_SCOPE_CONNECTION }
2944a6fe838SGlenn Strauss      ,{ CONST_STR_LEN("proxy.replace-http-host"),
2954a6fe838SGlenn Strauss         T_CONFIG_BOOL,
2964a6fe838SGlenn Strauss         T_CONFIG_SCOPE_CONNECTION }
2974a6fe838SGlenn Strauss      ,{ NULL, 0,
2984a6fe838SGlenn Strauss         T_CONFIG_UNSET,
2994a6fe838SGlenn Strauss         T_CONFIG_SCOPE_UNSET }
300bcdc6a3bSJan Kneschke     };
301bcdc6a3bSJan Kneschke 
3024a6fe838SGlenn Strauss     plugin_data * const p = p_d;
3034a6fe838SGlenn Strauss     if (!config_plugin_values_init(srv, p, cpk, "mod_proxy"))
3044a6fe838SGlenn Strauss         return HANDLER_ERROR;
305bcdc6a3bSJan Kneschke 
3064a6fe838SGlenn Strauss     /* process and validate config directives
3074a6fe838SGlenn Strauss      * (init i to 0 if global context; to 1 to skip empty global context) */
3084a6fe838SGlenn Strauss     for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
3094a6fe838SGlenn Strauss         config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
3104a6fe838SGlenn Strauss         gw_plugin_config *gw = NULL;
3114a6fe838SGlenn Strauss         for (; -1 != cpv->k_id; ++cpv) {
3124a6fe838SGlenn Strauss             switch (cpv->k_id) {
3134a6fe838SGlenn Strauss               case 0: /* proxy.server */
314*5e14db43SGlenn Strauss                 gw = ck_calloc(1, sizeof(gw_plugin_config));
3154a6fe838SGlenn Strauss                 if (!gw_set_defaults_backend(srv, (gw_plugin_data *)p, cpv->v.a,
3164a6fe838SGlenn Strauss                                              gw, 0, cpk[cpv->k_id].k)) {
3174a6fe838SGlenn Strauss                     gw_plugin_config_free(gw);
318bcdc6a3bSJan Kneschke                     return HANDLER_ERROR;
319bcdc6a3bSJan Kneschke                 }
3204a6fe838SGlenn Strauss                 /* error if "mode" = "authorizer";
3214a6fe838SGlenn Strauss                  * proxy can not act as authorizer */
3224a6fe838SGlenn Strauss                 /*(check after gw_set_defaults_backend())*/
3234a6fe838SGlenn Strauss                 if (gw->exts_auth && gw->exts_auth->used) {
3244a6fe838SGlenn Strauss                     log_error(srv->errh, __FILE__, __LINE__,
3254a6fe838SGlenn Strauss                       "%s must not define any hosts with "
3264a6fe838SGlenn Strauss                       "attribute \"mode\" = \"authorizer\"", cpk[cpv->k_id].k);
3274a6fe838SGlenn Strauss                     gw_plugin_config_free(gw);
328bca076c9SJan Kneschke                     return HANDLER_ERROR;
329bca076c9SJan Kneschke                 }
3304a6fe838SGlenn Strauss                 cpv->v.v = gw;
3314a6fe838SGlenn Strauss                 cpv->vtype = T_CONFIG_LOCAL;
3324a6fe838SGlenn Strauss                 break;
3334a6fe838SGlenn Strauss               case 1: /* proxy.balance */
3344a6fe838SGlenn Strauss                 cpv->v.u = (unsigned int)gw_get_defaults_balance(srv, cpv->v.b);
3354a6fe838SGlenn Strauss                 break;
3364a6fe838SGlenn Strauss               case 2: /* proxy.debug */
3374a6fe838SGlenn Strauss               case 3: /* proxy.map-extensions */
3384a6fe838SGlenn Strauss                 break;
3394a6fe838SGlenn Strauss               case 4: /* proxy.forwarded */
3404a6fe838SGlenn Strauss                 cpv->v.u = mod_proxy_parse_forwarded(srv, cpv->v.a);
3414a6fe838SGlenn Strauss                 if (UINT_MAX == cpv->v.u) return HANDLER_ERROR;
3424a6fe838SGlenn Strauss                 cpv->vtype = T_CONFIG_LOCAL;
3434a6fe838SGlenn Strauss                 break;
3444a6fe838SGlenn Strauss               case 5: /* proxy.header */
3454a6fe838SGlenn Strauss                 cpv->v.v = mod_proxy_parse_header_opts(srv, cpv->v.a);
3464a6fe838SGlenn Strauss                 if (NULL == cpv->v.v) return HANDLER_ERROR;
3474a6fe838SGlenn Strauss                 cpv->vtype = T_CONFIG_LOCAL;
3484a6fe838SGlenn Strauss                 break;
3494a6fe838SGlenn Strauss               case 6: /* proxy.replace-http-host */
3504a6fe838SGlenn Strauss                 break;
3514a6fe838SGlenn Strauss               default:/* should not happen */
3524a6fe838SGlenn Strauss                 break;
3534a6fe838SGlenn Strauss             }
3544a6fe838SGlenn Strauss         }
35545b970e6SGlenn Strauss 
35645b970e6SGlenn Strauss         /* disable check-local for all exts (default enabled) */
3574a6fe838SGlenn Strauss         if (gw && gw->exts) { /*(check after gw_set_defaults_backend())*/
358cec18f43SGlenn Strauss             gw_exts_clear_check_local(gw->exts);
35945b970e6SGlenn Strauss         }
360b2e2d42cSGlenn Strauss     }
361b2e2d42cSGlenn Strauss 
3624a6fe838SGlenn Strauss     /* default is 0 */
3634a6fe838SGlenn Strauss     /*p->defaults.balance = (unsigned int)gw_get_defaults_balance(srv, NULL);*/
3644a6fe838SGlenn Strauss 
365fe5740d5SGlenn Strauss     p->defaults.header.force_http10 =
3664d99d9b7SGlenn Strauss       config_feature_bool(srv, "proxy.force-http10", 0);
367fe5740d5SGlenn Strauss 
3684a6fe838SGlenn Strauss     /* initialize p->defaults from global config context */
3694a6fe838SGlenn Strauss     if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
3704a6fe838SGlenn Strauss         const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
3714a6fe838SGlenn Strauss         if (-1 != cpv->k_id)
3724a6fe838SGlenn Strauss             mod_proxy_merge_config(&p->defaults, cpv);
373bcdc6a3bSJan Kneschke     }
374bcdc6a3bSJan Kneschke 
3754a6fe838SGlenn Strauss     /* special-case behavior if mod_extforward is loaded */
3764a6fe838SGlenn Strauss     for (uint32_t i = 0; i < srv->srvconf.modules->used; ++i) {
3774a6fe838SGlenn Strauss         buffer *m = &((data_string *)srv->srvconf.modules->data[i])->value;
3784a6fe838SGlenn Strauss         if (buffer_eq_slen(m, CONST_STR_LEN("mod_extforward"))) {
379b2e2d42cSGlenn Strauss             proxy_check_extforward = 1;
380b2e2d42cSGlenn Strauss             break;
381b2e2d42cSGlenn Strauss         }
382b2e2d42cSGlenn Strauss     }
383b2e2d42cSGlenn Strauss 
384bcdc6a3bSJan Kneschke     return HANDLER_GO_ON;
385bcdc6a3bSJan Kneschke }
386bcdc6a3bSJan Kneschke 
387fa67918dSGlenn Strauss 
388036d3d3dSGlenn Strauss /* (future: might move to http-header-glue.c) */
http_header_remap_host_match(buffer * b,size_t off,http_header_remap_opts * remap_hdrs,int is_req,size_t alen)389036d3d3dSGlenn Strauss static const buffer * http_header_remap_host_match (buffer *b, size_t off, http_header_remap_opts *remap_hdrs, int is_req, size_t alen)
390036d3d3dSGlenn Strauss {
391036d3d3dSGlenn Strauss     const array *hosts = is_req
392036d3d3dSGlenn Strauss       ? remap_hdrs->hosts_request
393036d3d3dSGlenn Strauss       : remap_hdrs->hosts_response;
394036d3d3dSGlenn Strauss     if (hosts) {
395036d3d3dSGlenn Strauss         const char * const s = b->ptr+off;
396036d3d3dSGlenn Strauss         for (size_t i = 0, used = hosts->used; i < used; ++i) {
397036d3d3dSGlenn Strauss             const data_string * const ds = (data_string *)hosts->data[i];
398ad9b7e00SGlenn Strauss             const buffer *k = &ds->key;
399af3df29aSGlenn Strauss             size_t mlen = buffer_clen(k);
400036d3d3dSGlenn Strauss             if (1 == mlen && k->ptr[0] == '-') {
401036d3d3dSGlenn Strauss                 /* match with authority provided in Host (if is_req)
402036d3d3dSGlenn Strauss                  * (If no Host in client request, then matching against empty
403036d3d3dSGlenn Strauss                  *  string will probably not match, and no remap will be
404036d3d3dSGlenn Strauss                  *  performed) */
405036d3d3dSGlenn Strauss                 k = is_req
406036d3d3dSGlenn Strauss                   ? remap_hdrs->http_host
407036d3d3dSGlenn Strauss                   : remap_hdrs->forwarded_host;
408036d3d3dSGlenn Strauss                 if (NULL == k) continue;
409af3df29aSGlenn Strauss                 mlen = buffer_clen(k);
410036d3d3dSGlenn Strauss             }
411e20b5318SGlenn Strauss             if (buffer_eq_icase_ss(s, alen, k->ptr, mlen)) {
412601c572cSGlenn Strauss                 if (buffer_is_equal_string(&ds->value, CONST_STR_LEN("-"))) {
413036d3d3dSGlenn Strauss                     return remap_hdrs->http_host;
414036d3d3dSGlenn Strauss                 }
415af3df29aSGlenn Strauss                 else if (!buffer_is_blank(&ds->value)) {
416036d3d3dSGlenn Strauss                     /*(save first matched request host for response match)*/
417036d3d3dSGlenn Strauss                     if (is_req && NULL == remap_hdrs->forwarded_host)
418601c572cSGlenn Strauss                         remap_hdrs->forwarded_host = &ds->value;
419601c572cSGlenn Strauss                     return &ds->value;
420036d3d3dSGlenn Strauss                 } /*(else leave authority as-is and stop matching)*/
421036d3d3dSGlenn Strauss                 break;
422036d3d3dSGlenn Strauss             }
423036d3d3dSGlenn Strauss         }
424036d3d3dSGlenn Strauss     }
425036d3d3dSGlenn Strauss     return NULL;
426036d3d3dSGlenn Strauss }
427036d3d3dSGlenn Strauss 
428036d3d3dSGlenn Strauss 
429036d3d3dSGlenn Strauss /* (future: might move to http-header-glue.c) */
http_header_remap_host(buffer * b,size_t off,http_header_remap_opts * remap_hdrs,int is_req,size_t alen)430036d3d3dSGlenn Strauss static size_t http_header_remap_host (buffer *b, size_t off, http_header_remap_opts *remap_hdrs, int is_req, size_t alen)
431036d3d3dSGlenn Strauss {
432036d3d3dSGlenn Strauss     const buffer * const m =
433036d3d3dSGlenn Strauss       http_header_remap_host_match(b, off, remap_hdrs, is_req, alen);
434036d3d3dSGlenn Strauss     if (NULL == m) return alen; /*(no match; return original authority length)*/
435036d3d3dSGlenn Strauss 
436036d3d3dSGlenn Strauss     buffer_substr_replace(b, off, alen, m);
437af3df29aSGlenn Strauss     return buffer_clen(m); /*(length of replacement authority)*/
438036d3d3dSGlenn Strauss }
439036d3d3dSGlenn Strauss 
440036d3d3dSGlenn Strauss 
441036d3d3dSGlenn Strauss /* (future: might move to http-header-glue.c) */
http_header_remap_urlpath(buffer * b,size_t off,http_header_remap_opts * remap_hdrs,int is_req)44226fb8d3eSGlenn Strauss static size_t http_header_remap_urlpath (buffer *b, size_t off, http_header_remap_opts *remap_hdrs, int is_req)
443036d3d3dSGlenn Strauss {
444036d3d3dSGlenn Strauss     const array *urlpaths = remap_hdrs->urlpaths;
445036d3d3dSGlenn Strauss     if (urlpaths) {
446036d3d3dSGlenn Strauss         const char * const s = b->ptr+off;
447af3df29aSGlenn Strauss         const size_t plen = buffer_clen(b) - off; /*(urlpath len)*/
448036d3d3dSGlenn Strauss         if (is_req) { /* request */
449036d3d3dSGlenn Strauss             for (size_t i = 0, used = urlpaths->used; i < used; ++i) {
450036d3d3dSGlenn Strauss                 const data_string * const ds = (data_string *)urlpaths->data[i];
451af3df29aSGlenn Strauss                 const size_t mlen = buffer_clen(&ds->key);
452ad9b7e00SGlenn Strauss                 if (mlen <= plen && 0 == memcmp(s, ds->key.ptr, mlen)) {
453036d3d3dSGlenn Strauss                     if (NULL == remap_hdrs->forwarded_urlpath)
454036d3d3dSGlenn Strauss                         remap_hdrs->forwarded_urlpath = ds;
455601c572cSGlenn Strauss                     buffer_substr_replace(b, off, mlen, &ds->value);
456af3df29aSGlenn Strauss                     return buffer_clen(&ds->value);/*(replacement len)*/
457036d3d3dSGlenn Strauss                 }
458036d3d3dSGlenn Strauss             }
459036d3d3dSGlenn Strauss         }
460036d3d3dSGlenn Strauss         else {        /* response; perform reverse map */
461036d3d3dSGlenn Strauss             if (NULL != remap_hdrs->forwarded_urlpath) {
462036d3d3dSGlenn Strauss                 const data_string * const ds = remap_hdrs->forwarded_urlpath;
463af3df29aSGlenn Strauss                 const size_t mlen = buffer_clen(&ds->value);
464601c572cSGlenn Strauss                 if (mlen <= plen && 0 == memcmp(s, ds->value.ptr, mlen)) {
465ad9b7e00SGlenn Strauss                     buffer_substr_replace(b, off, mlen, &ds->key);
466af3df29aSGlenn Strauss                     return buffer_clen(&ds->key); /*(replacement len)*/
467036d3d3dSGlenn Strauss                 }
468036d3d3dSGlenn Strauss             }
469036d3d3dSGlenn Strauss             for (size_t i = 0, used = urlpaths->used; i < used; ++i) {
470036d3d3dSGlenn Strauss                 const data_string * const ds = (data_string *)urlpaths->data[i];
471af3df29aSGlenn Strauss                 const size_t mlen = buffer_clen(&ds->value);
472601c572cSGlenn Strauss                 if (mlen <= plen && 0 == memcmp(s, ds->value.ptr, mlen)) {
473ad9b7e00SGlenn Strauss                     buffer_substr_replace(b, off, mlen, &ds->key);
474af3df29aSGlenn Strauss                     return buffer_clen(&ds->key); /*(replacement len)*/
475036d3d3dSGlenn Strauss                 }
476036d3d3dSGlenn Strauss             }
477036d3d3dSGlenn Strauss         }
478036d3d3dSGlenn Strauss     }
47926fb8d3eSGlenn Strauss     return 0;
480036d3d3dSGlenn Strauss }
481036d3d3dSGlenn Strauss 
482036d3d3dSGlenn Strauss 
483036d3d3dSGlenn Strauss /* (future: might move to http-header-glue.c) */
http_header_remap_uri(buffer * b,size_t off,http_header_remap_opts * remap_hdrs,int is_req)484036d3d3dSGlenn Strauss static void http_header_remap_uri (buffer *b, size_t off, http_header_remap_opts *remap_hdrs, int is_req)
485036d3d3dSGlenn Strauss {
486036d3d3dSGlenn Strauss     /* find beginning of URL-path (might be preceded by scheme://authority
487036d3d3dSGlenn Strauss      * (caller should make sure any leading whitespace is prior to offset) */
488036d3d3dSGlenn Strauss     if (b->ptr[off] != '/') {
489036d3d3dSGlenn Strauss         char *s = b->ptr+off;
490036d3d3dSGlenn Strauss         size_t alen; /*(authority len (host len))*/
491036d3d3dSGlenn Strauss         size_t slen; /*(scheme len)*/
492036d3d3dSGlenn Strauss         const buffer *m;
493036d3d3dSGlenn Strauss         /* skip over scheme and authority of URI to find beginning of URL-path
494036d3d3dSGlenn Strauss          * (value might conceivably be relative URL-path instead of URI) */
495036d3d3dSGlenn Strauss         if (NULL == (s = strchr(s, ':')) || s[1] != '/' || s[2] != '/') return;
496036d3d3dSGlenn Strauss         slen = s - (b->ptr+off);
497036d3d3dSGlenn Strauss         s += 3;
498036d3d3dSGlenn Strauss         off = (size_t)(s - b->ptr);
499036d3d3dSGlenn Strauss         if (NULL != (s = strchr(s, '/'))) {
500036d3d3dSGlenn Strauss             alen = (size_t)(s - b->ptr) - off;
501036d3d3dSGlenn Strauss             if (0 == alen) return; /*(empty authority, e.g. "http:///")*/
502036d3d3dSGlenn Strauss         }
503036d3d3dSGlenn Strauss         else {
504af3df29aSGlenn Strauss             alen = buffer_clen(b) - off;
505036d3d3dSGlenn Strauss             if (0 == alen) return; /*(empty authority, e.g. "http:///")*/
506f2610d23SGlenn Strauss             buffer_append_char(b, '/');
507036d3d3dSGlenn Strauss         }
508036d3d3dSGlenn Strauss 
509036d3d3dSGlenn Strauss         /* remap authority (if configured) and set offset to url-path */
510036d3d3dSGlenn Strauss         m = http_header_remap_host_match(b, off, remap_hdrs, is_req, alen);
511036d3d3dSGlenn Strauss         if (NULL != m) {
512036d3d3dSGlenn Strauss             if (remap_hdrs->https_remap
513036d3d3dSGlenn Strauss                 && (is_req ? 5==slen && 0==memcmp(b->ptr+off-slen-3,"https",5)
514036d3d3dSGlenn Strauss                            : 4==slen && 0==memcmp(b->ptr+off-slen-3,"http",4))){
515036d3d3dSGlenn Strauss                 if (is_req) {
516036d3d3dSGlenn Strauss                     memcpy(b->ptr+off-slen-3+4,"://",3);  /*("https"=>"http")*/
517036d3d3dSGlenn Strauss                     --off;
518036d3d3dSGlenn Strauss                     ++alen;
519036d3d3dSGlenn Strauss                 }
520036d3d3dSGlenn Strauss                 else {/*(!is_req)*/
521036d3d3dSGlenn Strauss                     memcpy(b->ptr+off-slen-3+4,"s://",4); /*("http" =>"https")*/
522036d3d3dSGlenn Strauss                     ++off;
523036d3d3dSGlenn Strauss                     --alen;
524036d3d3dSGlenn Strauss                 }
525036d3d3dSGlenn Strauss             }
526036d3d3dSGlenn Strauss             buffer_substr_replace(b, off, alen, m);
527af3df29aSGlenn Strauss             alen = buffer_clen(m);/*(length of replacement authority)*/
528036d3d3dSGlenn Strauss         }
529036d3d3dSGlenn Strauss         off += alen;
530036d3d3dSGlenn Strauss     }
531036d3d3dSGlenn Strauss 
532036d3d3dSGlenn Strauss     /* remap URLs (if configured) */
533036d3d3dSGlenn Strauss     http_header_remap_urlpath(b, off, remap_hdrs, is_req);
534036d3d3dSGlenn Strauss }
535036d3d3dSGlenn Strauss 
536036d3d3dSGlenn Strauss 
537036d3d3dSGlenn Strauss /* (future: might move to http-header-glue.c) */
http_header_remap_setcookie(buffer * b,size_t off,http_header_remap_opts * remap_hdrs)538036d3d3dSGlenn Strauss static void http_header_remap_setcookie (buffer *b, size_t off, http_header_remap_opts *remap_hdrs)
539036d3d3dSGlenn Strauss {
540036d3d3dSGlenn Strauss     /* Given the special-case of Set-Cookie and the (too) loosely restricted
541036d3d3dSGlenn Strauss      * characters allowed, for best results, the Set-Cookie value should be the
542036d3d3dSGlenn Strauss      * entire string in b from offset to end of string.  In response headers,
543036d3d3dSGlenn Strauss      * lighttpd may concatenate multiple Set-Cookie headers into single entry
5447c7f8c46SGlenn Strauss      * in r->resp_headers, separated by "\r\nSet-Cookie: " */
54526fb8d3eSGlenn Strauss     for (char *s = b->ptr+off, *e; *s; s = e) {
546036d3d3dSGlenn Strauss         size_t len;
54726fb8d3eSGlenn Strauss         {
54826fb8d3eSGlenn Strauss             while (*s != ';' && *s != '\n' && *s != '\0') ++s;
54926fb8d3eSGlenn Strauss             if (*s == '\n') {
55026fb8d3eSGlenn Strauss                 /*(include +1 for '\n', but leave ' ' for ++s below)*/
55126fb8d3eSGlenn Strauss                 s += sizeof("Set-Cookie:");
552036d3d3dSGlenn Strauss             }
55326fb8d3eSGlenn Strauss             if ('\0' == *s) return;
554036d3d3dSGlenn Strauss             do { ++s; } while (*s == ' ' || *s == '\t');
55569aeaf2fSGlenn Strauss             if ('\0' == *s) return;
55626fb8d3eSGlenn Strauss             e = s+1;
55726fb8d3eSGlenn Strauss             if ('=' == *s) continue;
558036d3d3dSGlenn Strauss             /*(interested only in Domain and Path attributes)*/
55926fb8d3eSGlenn Strauss             while (*e != '=' && *e != '\0') ++e;
56026fb8d3eSGlenn Strauss             if ('\0' == *e) return;
561036d3d3dSGlenn Strauss             ++e;
562036d3d3dSGlenn Strauss             switch ((int)(e - s - 1)) {
563036d3d3dSGlenn Strauss               case 4:
564e20b5318SGlenn Strauss                 if (buffer_eq_icase_ssn(s, "path", 4)) {
565036d3d3dSGlenn Strauss                     if (*e == '"') ++e;
566036d3d3dSGlenn Strauss                     if (*e != '/') continue;
567036d3d3dSGlenn Strauss                     off = (size_t)(e - b->ptr);
56826fb8d3eSGlenn Strauss                     len = http_header_remap_urlpath(b, off, remap_hdrs, 0);
56926fb8d3eSGlenn Strauss                     e = b->ptr+off+len; /*(b may have been reallocated)*/
570036d3d3dSGlenn Strauss                     continue;
571036d3d3dSGlenn Strauss                 }
572036d3d3dSGlenn Strauss                 break;
573036d3d3dSGlenn Strauss               case 6:
574e20b5318SGlenn Strauss                 if (buffer_eq_icase_ssn(s, "domain", 6)) {
575036d3d3dSGlenn Strauss                     size_t alen = 0;
576036d3d3dSGlenn Strauss                     if (*e == '"') ++e;
577036d3d3dSGlenn Strauss                     if (*e == '.') ++e;
578036d3d3dSGlenn Strauss                     if (*e == ';') continue;
579036d3d3dSGlenn Strauss                     off = (size_t)(e - b->ptr);
580036d3d3dSGlenn Strauss                     for (char c; (c = e[alen]) != ';' && c != ' ' && c != '\t'
581036d3d3dSGlenn Strauss                                           && c != '\r' && c != '\0'; ++alen);
582036d3d3dSGlenn Strauss                     len = http_header_remap_host(b, off, remap_hdrs, 0, alen);
583036d3d3dSGlenn Strauss                     e = b->ptr+off+len; /*(b may have been reallocated)*/
584036d3d3dSGlenn Strauss                     continue;
585036d3d3dSGlenn Strauss                 }
586036d3d3dSGlenn Strauss                 break;
587036d3d3dSGlenn Strauss               default:
588036d3d3dSGlenn Strauss                 break;
589036d3d3dSGlenn Strauss             }
590036d3d3dSGlenn Strauss         }
591036d3d3dSGlenn Strauss     }
592036d3d3dSGlenn Strauss }
593036d3d3dSGlenn Strauss 
594036d3d3dSGlenn Strauss 
buffer_append_string_backslash_escaped(buffer * b,const char * s,size_t len)595b2e2d42cSGlenn Strauss static void buffer_append_string_backslash_escaped(buffer *b, const char *s, size_t len) {
596b2e2d42cSGlenn Strauss     /* (future: might move to buffer.c) */
597b2e2d42cSGlenn Strauss     size_t j = 0;
598af3df29aSGlenn Strauss     char * const p = buffer_string_prepare_append(b, len*2 + 4);
599b2e2d42cSGlenn Strauss 
600b2e2d42cSGlenn Strauss     for (size_t i = 0; i < len; ++i) {
601440b3719SGlenn Strauss         int c = s[i];
602b2e2d42cSGlenn Strauss         if (c == '"' || c == '\\' || c == 0x7F || (c < 0x20 && c != '\t'))
603b2e2d42cSGlenn Strauss             p[j++] = '\\';
604440b3719SGlenn Strauss         p[j++] = c;
60593693b11SJan Kneschke     }
60693693b11SJan Kneschke 
607b2e2d42cSGlenn Strauss     buffer_commit(b, j);
608b2e2d42cSGlenn Strauss }
609b2e2d42cSGlenn Strauss 
proxy_set_Forwarded(connection * const con,request_st * const r,const unsigned int flags)6107c7f8c46SGlenn Strauss static void proxy_set_Forwarded(connection * const con, request_st * const r, const unsigned int flags) {
611e447de1bSGlenn Strauss     buffer *b = NULL;
612e447de1bSGlenn Strauss     buffer * const efor = (proxy_check_extforward)
613e447de1bSGlenn Strauss       ? http_header_env_get(r, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_FOR"))
614e447de1bSGlenn Strauss       : NULL;
615b2e2d42cSGlenn Strauss     int semicolon = 0;
616b2e2d42cSGlenn Strauss 
617b2e2d42cSGlenn Strauss     /* note: set "Forwarded" prior to updating X-Forwarded-For (below) */
618b2e2d42cSGlenn Strauss 
619b2e2d42cSGlenn Strauss     if (flags)
6207c7f8c46SGlenn Strauss         b = http_header_request_get(r, HTTP_HEADER_FORWARDED, CONST_STR_LEN("Forwarded"));
621b2e2d42cSGlenn Strauss 
6223dd3cde9SGlenn Strauss     if (flags && NULL == b) {
6236eb34ef5SGlenn Strauss         const buffer *xff =
6247c7f8c46SGlenn Strauss           http_header_request_get(r, HTTP_HEADER_X_FORWARDED_FOR, CONST_STR_LEN("X-Forwarded-For"));
62526f354cbSGlenn Strauss         b = http_header_request_set_ptr(r, HTTP_HEADER_FORWARDED,
62626f354cbSGlenn Strauss                                         CONST_STR_LEN("Forwarded"));
6273dd3cde9SGlenn Strauss         if (NULL != xff) {
628b2e2d42cSGlenn Strauss             /* use X-Forwarded-For contents to seed Forwarded */
6293dd3cde9SGlenn Strauss             char *s = xff->ptr;
630af3df29aSGlenn Strauss             size_t used = buffer_clen(xff);
631b2e2d42cSGlenn Strauss             for (size_t i=0, j, ipv6; i < used; ++i) {
632b2e2d42cSGlenn Strauss                 while (s[i] == ' ' || s[i] == '\t' || s[i] == ',') ++i;
633b2e2d42cSGlenn Strauss                 if (s[i] == '\0') break;
634b2e2d42cSGlenn Strauss                 j = i;
635b2e2d42cSGlenn Strauss                 do {
636b2e2d42cSGlenn Strauss                     ++i;
637b2e2d42cSGlenn Strauss                 } while (s[i]!=' ' && s[i]!='\t' && s[i]!=',' && s[i]!='\0');
638b2e2d42cSGlenn Strauss                 /* over-simplified test expecting only IPv4 or IPv6 addresses,
639b2e2d42cSGlenn Strauss                  * (not expecting :port, so treat existence of colon as IPv6,
640b2e2d42cSGlenn Strauss                  *  and not expecting unix paths, especially not containing ':')
641b2e2d42cSGlenn Strauss                  * quote all strings, backslash-escape since IPs not validated*/
642b2e2d42cSGlenn Strauss                 ipv6 = (NULL != memchr(s+j, ':', i-j)); /*(over-simplified) */
64326f354cbSGlenn Strauss                 ipv6
64426f354cbSGlenn Strauss                   ? buffer_append_string_len(b, CONST_STR_LEN("for=\"["))
64526f354cbSGlenn Strauss                   : buffer_append_string_len(b, CONST_STR_LEN("for=\""));
6463dd3cde9SGlenn Strauss                 buffer_append_string_backslash_escaped(b, s+j, i-j);
64726f354cbSGlenn Strauss                 ipv6
64826f354cbSGlenn Strauss                   ? buffer_append_string_len(b, CONST_STR_LEN("]\", "))
64926f354cbSGlenn Strauss                   : buffer_append_string_len(b, CONST_STR_LEN("\", "));
650b2e2d42cSGlenn Strauss             }
651b2e2d42cSGlenn Strauss         }
6523dd3cde9SGlenn Strauss     } else if (flags) { /*(NULL != b)*/
6533dd3cde9SGlenn Strauss         buffer_append_string_len(b, CONST_STR_LEN(", "));
654b2e2d42cSGlenn Strauss     }
655b2e2d42cSGlenn Strauss 
656b2e2d42cSGlenn Strauss     if (flags & PROXY_FORWARDED_FOR) {
6571367f606SGlenn Strauss         int family = sock_addr_get_family(&con->dst_addr);
6583dd3cde9SGlenn Strauss         buffer_append_string_len(b, CONST_STR_LEN("for="));
6593dd3cde9SGlenn Strauss         if (NULL != efor) {
660b2e2d42cSGlenn Strauss             /* over-simplified test expecting only IPv4 or IPv6 addresses,
661b2e2d42cSGlenn Strauss              * (not expecting :port, so treat existence of colon as IPv6,
662b2e2d42cSGlenn Strauss              *  and not expecting unix paths, especially not containing ':')
663b2e2d42cSGlenn Strauss              * quote all strings and backslash-escape since IPs not validated
664b2e2d42cSGlenn Strauss              * (should be IP from original con->dst_addr_buf,
665b2e2d42cSGlenn Strauss              *  so trustable and without :port) */
6663dd3cde9SGlenn Strauss             int ipv6 = (NULL != strchr(efor->ptr, ':'));
66726f354cbSGlenn Strauss             ipv6
66826f354cbSGlenn Strauss               ? buffer_append_string_len(b, CONST_STR_LEN("\"["))
669f2610d23SGlenn Strauss               : buffer_append_char(b, '"');
670af3df29aSGlenn Strauss             buffer_append_string_backslash_escaped(b, BUF_PTR_LEN(efor));
67126f354cbSGlenn Strauss             ipv6
67226f354cbSGlenn Strauss               ? buffer_append_string_len(b, CONST_STR_LEN("]\""))
673f2610d23SGlenn Strauss               : buffer_append_char(b, '"');
6741367f606SGlenn Strauss         } else if (family == AF_INET) {
675b2e2d42cSGlenn Strauss             /*(Note: if :port is added, then must be quoted-string:
676b2e2d42cSGlenn Strauss              * e.g. for="...:port")*/
677f1e8a82fSGlenn Strauss             buffer_append_string_buffer(b, &con->dst_addr_buf);
6781367f606SGlenn Strauss         } else if (family == AF_INET6) {
679dc01487eSGlenn Strauss             buffer_append_str3(b, CONST_STR_LEN("\"["),
680f1e8a82fSGlenn Strauss                                   BUF_PTR_LEN(&con->dst_addr_buf),
681dc01487eSGlenn Strauss                                   CONST_STR_LEN("]\""));
682b2e2d42cSGlenn Strauss         } else {
683f2610d23SGlenn Strauss             buffer_append_char(b, '"');
684b2e2d42cSGlenn Strauss             buffer_append_string_backslash_escaped(
685f1e8a82fSGlenn Strauss               b, BUF_PTR_LEN(&con->dst_addr_buf));
686f2610d23SGlenn Strauss             buffer_append_char(b, '"');
687b2e2d42cSGlenn Strauss         }
688b2e2d42cSGlenn Strauss         semicolon = 1;
689b2e2d42cSGlenn Strauss     }
690b2e2d42cSGlenn Strauss 
691b2e2d42cSGlenn Strauss     if (flags & PROXY_FORWARDED_BY) {
6921367f606SGlenn Strauss         int family = sock_addr_get_family(&con->srv_socket->addr);
693b2e2d42cSGlenn Strauss         /* Note: getsockname() and inet_ntop() are expensive operations.
694b2e2d42cSGlenn Strauss          * (recommendation: do not to enable by=... unless required)
695b2e2d42cSGlenn Strauss          * future: might use con->srv_socket->srv_token if addr is not
696b2e2d42cSGlenn Strauss          *   INADDR_ANY or in6addr_any, but must omit optional :port
697b2e2d42cSGlenn Strauss          *   from con->srv_socket->srv_token for consistency */
698b2e2d42cSGlenn Strauss 
699f2610d23SGlenn Strauss         if (semicolon) buffer_append_char(b, ';');
70026f354cbSGlenn Strauss         buffer_append_string_len(b, CONST_STR_LEN("by=\""));
701b2e2d42cSGlenn Strauss       #ifdef HAVE_SYS_UN_H
7021367f606SGlenn Strauss         /* special-case: might need to encode unix domain socket path */
7031367f606SGlenn Strauss         if (family == AF_UNIX) {
704b2e2d42cSGlenn Strauss             buffer_append_string_backslash_escaped(
705af3df29aSGlenn Strauss               b, BUF_PTR_LEN(con->srv_socket->srv_token));
7061367f606SGlenn Strauss         }
7071367f606SGlenn Strauss         else
708b2e2d42cSGlenn Strauss       #endif
7091367f606SGlenn Strauss         {
7101367f606SGlenn Strauss             sock_addr addr;
7111367f606SGlenn Strauss             socklen_t addrlen = sizeof(addr);
7121367f606SGlenn Strauss             if (0 == getsockname(con->fd,(struct sockaddr *)&addr, &addrlen)) {
7133dd3cde9SGlenn Strauss                 sock_addr_stringify_append_buffer(b, &addr);
7141367f606SGlenn Strauss             }
715b2e2d42cSGlenn Strauss         }
716f2610d23SGlenn Strauss         buffer_append_char(b, '"');
717b2e2d42cSGlenn Strauss         semicolon = 1;
718b2e2d42cSGlenn Strauss     }
719b2e2d42cSGlenn Strauss 
720b2e2d42cSGlenn Strauss     if (flags & PROXY_FORWARDED_PROTO) {
721e447de1bSGlenn Strauss         const buffer * const eproto = (proxy_check_extforward)
722e447de1bSGlenn Strauss           ? http_header_env_get(r, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_PROTO"))
723e447de1bSGlenn Strauss           : NULL;
724b2e2d42cSGlenn Strauss         /* expecting "http" or "https"
725b2e2d42cSGlenn Strauss          * (not checking if quoted-string and encoding needed) */
726f2610d23SGlenn Strauss         if (semicolon) buffer_append_char(b, ';');
7273dd3cde9SGlenn Strauss         if (NULL != eproto) {
728af3df29aSGlenn Strauss             buffer_append_str2(b, CONST_STR_LEN("proto="), BUF_PTR_LEN(eproto));
729e33ec759SGlenn Strauss         } else if (con->srv_socket->is_ssl) {
73026f354cbSGlenn Strauss             buffer_append_string_len(b, CONST_STR_LEN("proto=https"));
731b2e2d42cSGlenn Strauss         } else {
73226f354cbSGlenn Strauss             buffer_append_string_len(b, CONST_STR_LEN("proto=http"));
733b2e2d42cSGlenn Strauss         }
734b2e2d42cSGlenn Strauss         semicolon = 1;
735b2e2d42cSGlenn Strauss     }
736b2e2d42cSGlenn Strauss 
737b2e2d42cSGlenn Strauss     if (flags & PROXY_FORWARDED_HOST) {
738e447de1bSGlenn Strauss         const buffer * const ehost = (proxy_check_extforward)
739e447de1bSGlenn Strauss           ? http_header_env_get(r, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_HOST"))
740e447de1bSGlenn Strauss           : NULL;
7413dd3cde9SGlenn Strauss         if (NULL != ehost) {
742b2e2d42cSGlenn Strauss             if (semicolon)
743f2610d23SGlenn Strauss                 buffer_append_char(b, ';');
7443dd3cde9SGlenn Strauss             buffer_append_string_len(b, CONST_STR_LEN("host=\""));
745b2e2d42cSGlenn Strauss             buffer_append_string_backslash_escaped(
746af3df29aSGlenn Strauss               b, BUF_PTR_LEN(ehost));
747f2610d23SGlenn Strauss             buffer_append_char(b, '"');
748b2e2d42cSGlenn Strauss             semicolon = 1;
749af3df29aSGlenn Strauss         } else if (r->http_host && !buffer_is_blank(r->http_host)) {
750b2e2d42cSGlenn Strauss             if (semicolon)
751f2610d23SGlenn Strauss                 buffer_append_char(b, ';');
7523dd3cde9SGlenn Strauss             buffer_append_string_len(b, CONST_STR_LEN("host=\""));
753b2e2d42cSGlenn Strauss             buffer_append_string_backslash_escaped(
754af3df29aSGlenn Strauss               b, BUF_PTR_LEN(r->http_host));
755f2610d23SGlenn Strauss             buffer_append_char(b, '"');
756b2e2d42cSGlenn Strauss             semicolon = 1;
757b2e2d42cSGlenn Strauss         }
758b2e2d42cSGlenn Strauss     }
759b2e2d42cSGlenn Strauss 
760b2e2d42cSGlenn Strauss     if (flags & PROXY_FORWARDED_REMOTE_USER) {
7616eb34ef5SGlenn Strauss         const buffer *remote_user =
7627c7f8c46SGlenn Strauss           http_header_env_get(r, CONST_STR_LEN("REMOTE_USER"));
763b2e2d42cSGlenn Strauss         if (NULL != remote_user) {
764b2e2d42cSGlenn Strauss             if (semicolon)
765f2610d23SGlenn Strauss                 buffer_append_char(b, ';');
7663dd3cde9SGlenn Strauss             buffer_append_string_len(b, CONST_STR_LEN("remote_user=\""));
767b2e2d42cSGlenn Strauss             buffer_append_string_backslash_escaped(
768af3df29aSGlenn Strauss               b, BUF_PTR_LEN(remote_user));
769f2610d23SGlenn Strauss             buffer_append_char(b, '"');
77028f1867cSGlenn Strauss             /*semicolon = 1;*/
771b2e2d42cSGlenn Strauss         }
772b2e2d42cSGlenn Strauss     }
773b2e2d42cSGlenn Strauss 
774b2e2d42cSGlenn Strauss     /* legacy X-* headers, including X-Forwarded-For */
775b2e2d42cSGlenn Strauss 
776f1e8a82fSGlenn Strauss     b = (NULL != efor) ? efor : &con->dst_addr_buf;
777e447de1bSGlenn Strauss     http_header_request_append(r, HTTP_HEADER_X_FORWARDED_FOR,
7783dd3cde9SGlenn Strauss                                CONST_STR_LEN("X-Forwarded-For"),
779af3df29aSGlenn Strauss                                BUF_PTR_LEN(b));
780b2e2d42cSGlenn Strauss 
781e447de1bSGlenn Strauss     b = r->http_host;
782af3df29aSGlenn Strauss     if (b && !buffer_is_blank(b)) {
7837c7f8c46SGlenn Strauss         http_header_request_set(r, HTTP_HEADER_OTHER,
7843dd3cde9SGlenn Strauss                                 CONST_STR_LEN("X-Host"),
785af3df29aSGlenn Strauss                                 BUF_PTR_LEN(b));
7867c7f8c46SGlenn Strauss         http_header_request_set(r, HTTP_HEADER_OTHER,
7873dd3cde9SGlenn Strauss                                 CONST_STR_LEN("X-Forwarded-Host"),
788af3df29aSGlenn Strauss                                 BUF_PTR_LEN(b));
789b2e2d42cSGlenn Strauss     }
790b2e2d42cSGlenn Strauss 
791e447de1bSGlenn Strauss     b = &r->uri.scheme;
7927c7f8c46SGlenn Strauss     http_header_request_set(r, HTTP_HEADER_X_FORWARDED_PROTO,
7933dd3cde9SGlenn Strauss                             CONST_STR_LEN("X-Forwarded-Proto"),
794af3df29aSGlenn Strauss                             BUF_PTR_LEN(b));
79593693b11SJan Kneschke }
79693693b11SJan Kneschke 
79793693b11SJan Kneschke 
proxy_stdin_append(gw_handler_ctx * hctx)798bcddbe18SGlenn Strauss static handler_t proxy_stdin_append(gw_handler_ctx *hctx) {
799bcddbe18SGlenn Strauss     /*handler_ctx *hctx = (handler_ctx *)gwhctx;*/
80081029b8bSGlenn Strauss     chunkqueue * const req_cq = &hctx->r->reqbody_queue;
80197e314fcSGlenn Strauss     const off_t req_cqlen = chunkqueue_length(req_cq);
802bcddbe18SGlenn Strauss     if (req_cqlen) {
803bcddbe18SGlenn Strauss         /* XXX: future: use http_chunk_len_append() */
804bcddbe18SGlenn Strauss         buffer * const tb = hctx->r->tmp_buf;
805bcddbe18SGlenn Strauss         buffer_clear(tb);
806bcddbe18SGlenn Strauss         buffer_append_uint_hex_lc(tb, (uintmax_t)req_cqlen);
807bcddbe18SGlenn Strauss         buffer_append_string_len(tb, CONST_STR_LEN("\r\n"));
808bcddbe18SGlenn Strauss 
809af3df29aSGlenn Strauss         const off_t len = (off_t)buffer_clen(tb)
810bcddbe18SGlenn Strauss                         + 2 /*(+2 end chunk "\r\n")*/
811bcddbe18SGlenn Strauss                         + req_cqlen;
812bcddbe18SGlenn Strauss         if (-1 != hctx->wb_reqlen)
813bcddbe18SGlenn Strauss             hctx->wb_reqlen += (hctx->wb_reqlen >= 0) ? len : -len;
814bcddbe18SGlenn Strauss 
81581029b8bSGlenn Strauss         (chunkqueue_is_empty(&hctx->wb) || hctx->wb.first->type == MEM_CHUNK)
816bcddbe18SGlenn Strauss                                           /* else FILE_CHUNK for temp file */
817af3df29aSGlenn Strauss           ? chunkqueue_append_mem(&hctx->wb, BUF_PTR_LEN(tb))
818af3df29aSGlenn Strauss           : chunkqueue_append_mem_min(&hctx->wb, BUF_PTR_LEN(tb));
81981029b8bSGlenn Strauss         chunkqueue_steal(&hctx->wb, req_cq, req_cqlen);
820bcddbe18SGlenn Strauss 
82181029b8bSGlenn Strauss         chunkqueue_append_mem_min(&hctx->wb, CONST_STR_LEN("\r\n"));
822bcddbe18SGlenn Strauss     }
823bcddbe18SGlenn Strauss 
82481029b8bSGlenn Strauss     if (hctx->wb.bytes_in == hctx->wb_reqlen) {/*hctx->r->reqbody_length >= 0*/
825bcddbe18SGlenn Strauss         /* terminate STDIN */
82681029b8bSGlenn Strauss         chunkqueue_append_mem(&hctx->wb, CONST_STR_LEN("0\r\n\r\n"));
827bcddbe18SGlenn Strauss         hctx->wb_reqlen += (int)sizeof("0\r\n\r\n");
828bcddbe18SGlenn Strauss     }
829bcddbe18SGlenn Strauss 
830bcddbe18SGlenn Strauss     return HANDLER_GO_ON;
831bcddbe18SGlenn Strauss }
832bcddbe18SGlenn Strauss 
833bcddbe18SGlenn Strauss 
proxy_create_env(gw_handler_ctx * gwhctx)83450bdb55dSGlenn Strauss static handler_t proxy_create_env(gw_handler_ctx *gwhctx) {
83545b970e6SGlenn Strauss 	handler_ctx *hctx = (handler_ctx *)gwhctx;
8367c7f8c46SGlenn Strauss 	request_st * const r = hctx->gw.r;
837a458c2e7SGlenn Strauss 	const int remap_headers = (NULL != hctx->conf.header.urlpaths
838a458c2e7SGlenn Strauss 				   || NULL != hctx->conf.header.hosts_request);
83981029b8bSGlenn Strauss 	size_t rsz = (size_t)(r->read_queue.bytes_out - hctx->gw.wb.bytes_in);
84081029b8bSGlenn Strauss 	if (rsz >= 65536) rsz = r->rqst_header_len;
84181029b8bSGlenn Strauss 	buffer * const b = chunkqueue_prepend_buffer_open_sz(&hctx->gw.wb, rsz);
842f410431bSGlenn Strauss 
843bcdc6a3bSJan Kneschke 	/* build header */
844bcdc6a3bSJan Kneschke 
845bcdc6a3bSJan Kneschke 	/* request line */
8465d1aa5d0SGlenn Strauss 	const buffer * const m =
8475d1aa5d0SGlenn Strauss 	  http_method_buf(!r->h2_connect_ext
8485d1aa5d0SGlenn Strauss 			  ? r->http_method
8495d1aa5d0SGlenn Strauss 			  : HTTP_METHOD_GET); /*(translate HTTP/2 CONNECT ext)*/
8509fe8fbaaSGlenn Strauss 	buffer_append_str3(b,
8519fe8fbaaSGlenn Strauss 	                   BUF_PTR_LEN(m),
8529fe8fbaaSGlenn Strauss 	                   CONST_STR_LEN(" "),
8539fe8fbaaSGlenn Strauss 	                   BUF_PTR_LEN(&r->target));
854036d3d3dSGlenn Strauss 	if (remap_headers)
855af3df29aSGlenn Strauss 		http_header_remap_uri(b, buffer_clen(b) - buffer_clen(&r->target),
856af3df29aSGlenn Strauss 		                      &hctx->conf.header, 1);
857bcddbe18SGlenn Strauss 
858af3df29aSGlenn Strauss 	buffer_append_string_len(b, !hctx->conf.header.force_http10
859af3df29aSGlenn Strauss 	                            ? " HTTP/1.1" : " HTTP/1.0",
860af3df29aSGlenn Strauss 	                            sizeof(" HTTP/1.1")-1);
861036d3d3dSGlenn Strauss 
862af3df29aSGlenn Strauss 	if (hctx->conf.replace_http_host && !buffer_is_blank(hctx->gw.host->id)) {
86345b970e6SGlenn Strauss 		if (hctx->gw.conf.debug > 1) {
8647c7f8c46SGlenn Strauss 			log_error(r->conf.errh, __FILE__, __LINE__,
865010c2894SGlenn Strauss 			  "proxy - using \"%s\" as HTTP Host", hctx->gw.host->id->ptr);
866879ce0b5SGlenn Strauss 		}
867dc01487eSGlenn Strauss 		buffer_append_str2(b, CONST_STR_LEN("\r\nHost: "),
868af3df29aSGlenn Strauss 		                      BUF_PTR_LEN(hctx->gw.host->id));
869af3df29aSGlenn Strauss 	} else if (r->http_host && !buffer_is_unset(r->http_host)) {
870dc01487eSGlenn Strauss 		buffer_append_str2(b, CONST_STR_LEN("\r\nHost: "),
871af3df29aSGlenn Strauss 		                      BUF_PTR_LEN(r->http_host));
872036d3d3dSGlenn Strauss 		if (remap_headers) {
873af3df29aSGlenn Strauss 			size_t alen = buffer_clen(r->http_host);
874af3df29aSGlenn Strauss 			http_header_remap_host(b, buffer_clen(b) - alen, &hctx->conf.header, 1, alen);
875036d3d3dSGlenn Strauss 		}
87682abd16dSGlenn Strauss 	} else {
87782abd16dSGlenn Strauss 		/* no Host header available; must send HTTP/1.0 request */
87882abd16dSGlenn Strauss 		b->ptr[b->used-2] = '0'; /*(overwrite end of request line)*/
879879ce0b5SGlenn Strauss 	}
880bcdc6a3bSJan Kneschke 
8817c7f8c46SGlenn Strauss 	if (r->reqbody_length > 0
8827c7f8c46SGlenn Strauss 	    || (0 == r->reqbody_length
8837c7f8c46SGlenn Strauss 		&& !http_method_get_or_head(r->http_method))) {
88437261bbdSGlenn Strauss 		/* set Content-Length if client sent Transfer-Encoding: chunked
88537261bbdSGlenn Strauss 		 * and not streaming to backend (request body has been fully received) */
8867c7f8c46SGlenn Strauss 		const buffer *vb = http_header_request_get(r, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length"));
8873dd3cde9SGlenn Strauss 		if (NULL == vb) {
88826f354cbSGlenn Strauss 			buffer_append_int(
88926f354cbSGlenn Strauss 			  http_header_request_set_ptr(r, HTTP_HEADER_CONTENT_LENGTH,
89026f354cbSGlenn Strauss 			                              CONST_STR_LEN("Content-Length")),
89126f354cbSGlenn Strauss 			  r->reqbody_length);
89237261bbdSGlenn Strauss 		}
89337261bbdSGlenn Strauss 	}
8945d1aa5d0SGlenn Strauss 	else if (r->h2_connect_ext) {
8955d1aa5d0SGlenn Strauss 	}
89618ed51f0SGlenn Strauss 	else if (-1 == r->reqbody_length
897f7919c1aSGlenn Strauss 	         && (r->conf.stream_request_body
898f7919c1aSGlenn Strauss 	             & (FDEVENT_STREAM_REQUEST | FDEVENT_STREAM_REQUEST_BUFMIN))) {
89918ed51f0SGlenn Strauss 		if (__builtin_expect( (hctx->conf.header.force_http10), 0))
90018ed51f0SGlenn Strauss 			return http_response_reqbody_read_error(r, 411);
901f7919c1aSGlenn Strauss 		hctx->gw.stdin_append = proxy_stdin_append; /* stream chunked body */
90238c87358SGlenn Strauss 		buffer_append_string_len(b, CONST_STR_LEN("\r\nTransfer-Encoding: chunked"));
903f7919c1aSGlenn Strauss 	}
90437261bbdSGlenn Strauss 
90518e96334SGlenn Strauss 	/* "Forwarded" and legacy X- headers */
90618e96334SGlenn Strauss 	proxy_set_Forwarded(r->con, r, hctx->conf.forwarded);
90718e96334SGlenn Strauss 
908bcdc6a3bSJan Kneschke 	/* request header */
909f7919c1aSGlenn Strauss 	const buffer *connhdr = NULL;
910891007fbSGlenn Strauss 	const buffer *te = NULL;
911891007fbSGlenn Strauss 	const buffer *upgrade = NULL;
9127c7f8c46SGlenn Strauss 	for (size_t i = 0, used = r->rqst_headers.used; i < used; ++i) {
913891007fbSGlenn Strauss 		const data_string * const ds = (data_string *)r->rqst_headers.data[i];
914891007fbSGlenn Strauss 		switch (ds->ext) {
915036d3d3dSGlenn Strauss 		default:
916036d3d3dSGlenn Strauss 			break;
91718e96334SGlenn Strauss 		case HTTP_HEADER_HOST:
91818e96334SGlenn Strauss 			continue; /*(handled further above)*/
91918e96334SGlenn Strauss 		case HTTP_HEADER_OTHER:
92018e96334SGlenn Strauss 			if (__builtin_expect( ('p' == (ds->key.ptr[0] | 0x20)), 0)) {
92118e96334SGlenn Strauss 				if (buffer_eq_icase_slen(&ds->key, CONST_STR_LEN("Proxy-Connection"))) continue;
92218e96334SGlenn Strauss 				/* Do not emit HTTP_PROXY in environment.
92318e96334SGlenn Strauss 				 * Some executables use HTTP_PROXY to configure
92418e96334SGlenn Strauss 				 * outgoing proxy.  See also https://httpoxy.org/ */
92518e96334SGlenn Strauss 				if (buffer_eq_icase_slen(&ds->key, CONST_STR_LEN("Proxy"))) continue;
92618e96334SGlenn Strauss 			}
92718e96334SGlenn Strauss 			break;
928891007fbSGlenn Strauss 		case HTTP_HEADER_TE:
929fe5740d5SGlenn Strauss 			if (hctx->conf.header.force_http10 || r->http_version == HTTP_VERSION_1_0) continue;
930f7919c1aSGlenn Strauss 			/* ignore if not exactly "trailers" */
931f7919c1aSGlenn Strauss 			if (!buffer_eq_icase_slen(&ds->value, CONST_STR_LEN("trailers"))) continue;
932af3df29aSGlenn Strauss 			/*if (!buffer_is_blank(&ds->value)) te = &ds->value;*/
933af3df29aSGlenn Strauss 			te = &ds->value; /*("trailers")*/
934f7919c1aSGlenn Strauss 			break;
935891007fbSGlenn Strauss 		case HTTP_HEADER_UPGRADE:
9365d1aa5d0SGlenn Strauss 			if (hctx->conf.header.force_http10
9375d1aa5d0SGlenn Strauss 			    || (r->http_version != HTTP_VERSION_1_1 && !r->h2_connect_ext))
9385d1aa5d0SGlenn Strauss 				continue;
939f7919c1aSGlenn Strauss 			if (!hctx->conf.header.upgrade) continue;
940af3df29aSGlenn Strauss 			if (!buffer_is_blank(&ds->value)) upgrade = &ds->value;
941f7919c1aSGlenn Strauss 			break;
942891007fbSGlenn Strauss 		case HTTP_HEADER_CONNECTION:
943891007fbSGlenn Strauss 			connhdr = &ds->value;
944891007fbSGlenn Strauss 			continue;
945891007fbSGlenn Strauss 		case HTTP_HEADER_SET_COOKIE:
946891007fbSGlenn Strauss 			continue; /*(response header only; avoid accidental reflection)*/
947bcdc6a3bSJan Kneschke 		}
948036d3d3dSGlenn Strauss 
949af3df29aSGlenn Strauss 		const uint32_t klen = buffer_clen(&ds->key);
950af3df29aSGlenn Strauss 		const uint32_t vlen = buffer_clen(&ds->value);
951891007fbSGlenn Strauss 		if (0 == klen || 0 == vlen) continue;
95238c87358SGlenn Strauss 		char * restrict s = buffer_extend(b, klen+vlen+4);
95338c87358SGlenn Strauss 		s[0] = '\r';
95438c87358SGlenn Strauss 		s[1] = '\n';
95538c87358SGlenn Strauss 		memcpy(s+2, ds->key.ptr, klen);
95638c87358SGlenn Strauss 		s += 2+klen;
95738c87358SGlenn Strauss 		s[0] = ':';
95838c87358SGlenn Strauss 		s[1] = ' ';
95938c87358SGlenn Strauss 		memcpy(s+2, ds->value.ptr, vlen);
960036d3d3dSGlenn Strauss 
961036d3d3dSGlenn Strauss 		if (!remap_headers) continue;
962036d3d3dSGlenn Strauss 
963036d3d3dSGlenn Strauss 		/* check for hdrs for which to remap URIs in-place after append to b */
964036d3d3dSGlenn Strauss 
965036d3d3dSGlenn Strauss 		switch (klen) {
966036d3d3dSGlenn Strauss 		default:
967036d3d3dSGlenn Strauss 			continue;
968036d3d3dSGlenn Strauss 	      #if 0 /* "URI" is HTTP response header (non-standard; historical in Apache) */
969036d3d3dSGlenn Strauss 		case 3:
970891007fbSGlenn Strauss 			if (ds->ext == HTTP_HEADER_OTHER && buffer_eq_icase_slen(&ds->key, CONST_STR_LEN("URI"))) break;
971036d3d3dSGlenn Strauss 			continue;
972036d3d3dSGlenn Strauss 	      #endif
973036d3d3dSGlenn Strauss 	      #if 0 /* "Location" is HTTP response header */
974036d3d3dSGlenn Strauss 		case 8:
975891007fbSGlenn Strauss 			if (ds->ext == HTTP_HEADER_LOCATION) break;
976036d3d3dSGlenn Strauss 			continue;
977036d3d3dSGlenn Strauss 	      #endif
978036d3d3dSGlenn Strauss 		case 11: /* "Destination" is WebDAV request header */
979891007fbSGlenn Strauss 			if (ds->ext == HTTP_HEADER_OTHER && buffer_eq_icase_slen(&ds->key, CONST_STR_LEN("Destination"))) break;
980036d3d3dSGlenn Strauss 			continue;
981036d3d3dSGlenn Strauss 		case 16: /* "Content-Location" may be HTTP request or response header */
982891007fbSGlenn Strauss 			if (ds->ext == HTTP_HEADER_CONTENT_LOCATION) break;
983036d3d3dSGlenn Strauss 			continue;
984036d3d3dSGlenn Strauss 		}
985036d3d3dSGlenn Strauss 
986af3df29aSGlenn Strauss 		http_header_remap_uri(b, buffer_clen(b) - vlen, &hctx->conf.header, 1);
987bcdc6a3bSJan Kneschke 	}
988bcdc6a3bSJan Kneschke 
989fe5740d5SGlenn Strauss 	if (connhdr && !hctx->conf.header.force_http10 && r->http_version >= HTTP_VERSION_1_1
990f7919c1aSGlenn Strauss 	    && !buffer_eq_icase_slen(connhdr, CONST_STR_LEN("close"))) {
991f7919c1aSGlenn Strauss 		/* mod_proxy always sends Connection: close to backend */
99238c87358SGlenn Strauss 		buffer_append_string_len(b, CONST_STR_LEN("\r\nConnection: close"));
993f7919c1aSGlenn Strauss 		/* (future: might be pedantic and also check Connection header for each
994f7919c1aSGlenn Strauss 		 * token using http_header_str_contains_token() */
995af3df29aSGlenn Strauss 		if (te)
996f7919c1aSGlenn Strauss 			buffer_append_string_len(b, CONST_STR_LEN(", te"));
997af3df29aSGlenn Strauss 		if (upgrade)
998f7919c1aSGlenn Strauss 			buffer_append_string_len(b, CONST_STR_LEN(", upgrade"));
999f7919c1aSGlenn Strauss 		buffer_append_string_len(b, CONST_STR_LEN("\r\n\r\n"));
1000f7919c1aSGlenn Strauss 	}
10015d1aa5d0SGlenn Strauss 	else if (r->h2_connect_ext) {
10025d1aa5d0SGlenn Strauss 		/* https://datatracker.ietf.org/doc/html/rfc6455#section-4.1
10035d1aa5d0SGlenn Strauss 		 * 7. The request MUST include a header field with the name
10045d1aa5d0SGlenn Strauss 		 *    |Sec-WebSocket-Key|.  The value of this header field MUST be a
10055d1aa5d0SGlenn Strauss 		 *    nonce consisting of a randomly selected 16-byte value that has
10065d1aa5d0SGlenn Strauss 		 *    been base64-encoded (see Section 4 of [RFC4648]).  The nonce
10075d1aa5d0SGlenn Strauss 		 *    MUST be selected randomly for each connection.
10085d1aa5d0SGlenn Strauss 		 * Note: Sec-WebSocket-Key is not used in RFC8441;
10095d1aa5d0SGlenn Strauss 		 *       include Sec-WebSocket-Key for HTTP/1.1 compatibility;
10105d1aa5d0SGlenn Strauss 		 *       !!not random!! base64-encoded "0000000000000000" */
10115d1aa5d0SGlenn Strauss 		if (!http_header_request_get(r, HTTP_HEADER_OTHER,
10125d1aa5d0SGlenn Strauss 		                             CONST_STR_LEN("Sec-WebSocket-Key")))
10135d1aa5d0SGlenn Strauss 			buffer_append_string_len(b, CONST_STR_LEN(
1014cda9b716SShulyaka 			  "\r\nSec-WebSocket-Key: MDAwMDAwMDAwMDAwMDAwMA=="));
10155d1aa5d0SGlenn Strauss 		buffer_append_string_len(b, CONST_STR_LEN(
10165d1aa5d0SGlenn Strauss 		                              "\r\nUpgrade: websocket"
10175d1aa5d0SGlenn Strauss 		                              "\r\nConnection: close, upgrade\r\n\r\n"));
10185d1aa5d0SGlenn Strauss 	}
1019f7919c1aSGlenn Strauss 	else    /* mod_proxy always sends Connection: close to backend */
102038c87358SGlenn Strauss 		buffer_append_string_len(b, CONST_STR_LEN("\r\nConnection: close\r\n\r\n"));
1021bcdc6a3bSJan Kneschke 
1022af3df29aSGlenn Strauss 	hctx->gw.wb_reqlen = buffer_clen(b);
102381029b8bSGlenn Strauss 	chunkqueue_prepend_buffer_commit(&hctx->gw.wb);
10241be163b4SStefan Bühler 
10257c7f8c46SGlenn Strauss 	if (r->reqbody_length) {
10267c7f8c46SGlenn Strauss 		if (r->reqbody_length > 0)
10277c7f8c46SGlenn Strauss 			hctx->gw.wb_reqlen += r->reqbody_length; /* total req size */
1028316e959bSGlenn Strauss 		else /* as-yet-unknown total request size (Transfer-Encoding: chunked)*/
102945b970e6SGlenn Strauss 			hctx->gw.wb_reqlen = -hctx->gw.wb_reqlen;
10302ecbe594SGlenn Strauss 		if (hctx->gw.stdin_append == proxy_stdin_append)
10312ecbe594SGlenn Strauss 			proxy_stdin_append(&hctx->gw);
10322ecbe594SGlenn Strauss 		else
10332ecbe594SGlenn Strauss 			chunkqueue_append_chunkqueue(&hctx->gw.wb, &r->reqbody_queue);
1034bcdc6a3bSJan Kneschke 	}
1035bcdc6a3bSJan Kneschke 
10362a7d3a27SGlenn Strauss 	plugin_stats_inc("proxy.requests");
103745b970e6SGlenn Strauss 	return HANDLER_GO_ON;
1038bcdc6a3bSJan Kneschke }
1039bcdc6a3bSJan Kneschke 
10403770df23SGlenn Strauss 
proxy_create_env_connect(gw_handler_ctx * gwhctx)104150bdb55dSGlenn Strauss static handler_t proxy_create_env_connect(gw_handler_ctx *gwhctx) {
10423770df23SGlenn Strauss 	handler_ctx *hctx = (handler_ctx *)gwhctx;
10437c7f8c46SGlenn Strauss 	request_st * const r = hctx->gw.r;
10447c7f8c46SGlenn Strauss 	r->http_status = 200; /* OK */
10457c7f8c46SGlenn Strauss 	r->resp_body_started = 1;
104650bdb55dSGlenn Strauss 	gw_set_transparent(&hctx->gw);
10477c7f8c46SGlenn Strauss 	http_response_upgrade_read_body_unknown(r);
10483770df23SGlenn Strauss 
10492a7d3a27SGlenn Strauss 	plugin_stats_inc("proxy.requests");
10503770df23SGlenn Strauss 	return HANDLER_GO_ON;
10513770df23SGlenn Strauss }
10523770df23SGlenn Strauss 
10533770df23SGlenn Strauss 
proxy_response_headers(request_st * const r,struct http_response_opts_t * opts)10547c7f8c46SGlenn Strauss static handler_t proxy_response_headers(request_st * const r, struct http_response_opts_t *opts) {
1055036d3d3dSGlenn Strauss     /* response headers just completed */
105645b970e6SGlenn Strauss     handler_ctx *hctx = (handler_ctx *)opts->pdata;
10579f82ba8fSGlenn Strauss     http_header_remap_opts * const remap_hdrs = &hctx->conf.header;
1058036d3d3dSGlenn Strauss 
10599c8981a7SGlenn Strauss     if (light_btst(r->resp_htags, HTTP_HEADER_UPGRADE)) {
10609f82ba8fSGlenn Strauss         if (remap_hdrs->upgrade && r->http_status == 101) {
106114656f8fSGlenn Strauss             /* 101 Switching Protocols; transition to transparent proxy */
10625d1aa5d0SGlenn Strauss             if (r->h2_connect_ext) {
10635d1aa5d0SGlenn Strauss                 r->http_status = 200; /* OK (response status for CONNECT) */
10645d1aa5d0SGlenn Strauss                 http_header_response_unset(r, HTTP_HEADER_UPGRADE,
10655d1aa5d0SGlenn Strauss                                            CONST_STR_LEN("Upgrade"));
10665d1aa5d0SGlenn Strauss                 http_header_response_unset(r, HTTP_HEADER_OTHER,
10675d1aa5d0SGlenn Strauss                                          CONST_STR_LEN("Sec-WebSocket-Accept"));
10685d1aa5d0SGlenn Strauss             }
106950bdb55dSGlenn Strauss             gw_set_transparent(&hctx->gw);
10707c7f8c46SGlenn Strauss             http_response_upgrade_read_body_unknown(r);
107114656f8fSGlenn Strauss         }
107214656f8fSGlenn Strauss         else {
10739c8981a7SGlenn Strauss             light_bclr(r->resp_htags, HTTP_HEADER_UPGRADE);
107414656f8fSGlenn Strauss           #if 0
107514656f8fSGlenn Strauss             /* preserve prior questionable behavior; likely broken behavior
107614656f8fSGlenn Strauss              * anyway if backend thinks connection is being upgraded but client
107714656f8fSGlenn Strauss              * does not receive Connection: upgrade */
10787c7f8c46SGlenn Strauss             http_header_response_unset(r, HTTP_HEADER_UPGRADE,
1079f13db690SGlenn Strauss                                        CONST_STR_LEN("Upgrade"))
108014656f8fSGlenn Strauss           #endif
108114656f8fSGlenn Strauss         }
108214656f8fSGlenn Strauss     }
10835d1aa5d0SGlenn Strauss     else if (__builtin_expect( (r->h2_connect_ext != 0), 0)
10845d1aa5d0SGlenn Strauss              && r->http_status < 300) {
10855d1aa5d0SGlenn Strauss         /*(not handling other 1xx intermediate responses here; not expected)*/
10865d1aa5d0SGlenn Strauss         http_response_body_clear(r, 0);
10875d1aa5d0SGlenn Strauss         r->handler_module = NULL;
10885d1aa5d0SGlenn Strauss         r->http_status = 405; /* Method Not Allowed */
10895d1aa5d0SGlenn Strauss         return HANDLER_FINISHED;
10905d1aa5d0SGlenn Strauss     }
109114656f8fSGlenn Strauss 
1092036d3d3dSGlenn Strauss     /* rewrite paths, if needed */
1093036d3d3dSGlenn Strauss 
10949f82ba8fSGlenn Strauss     if (NULL == remap_hdrs->urlpaths && NULL == remap_hdrs->hosts_response)
109545b970e6SGlenn Strauss         return HANDLER_GO_ON;
1096036d3d3dSGlenn Strauss 
10979f82ba8fSGlenn Strauss     buffer *vb;
10989c8981a7SGlenn Strauss     if (light_btst(r->resp_htags, HTTP_HEADER_LOCATION)) {
10999f82ba8fSGlenn Strauss         vb = http_header_response_get(r, HTTP_HEADER_LOCATION,
11009f82ba8fSGlenn Strauss                                          CONST_STR_LEN("Location"));
11019f82ba8fSGlenn Strauss         if (vb) http_header_remap_uri(vb, 0, remap_hdrs, 0);
1102036d3d3dSGlenn Strauss     }
11039c8981a7SGlenn Strauss     if (light_btst(r->resp_htags, HTTP_HEADER_CONTENT_LOCATION)) {
11049f82ba8fSGlenn Strauss         vb = http_header_response_get(r, HTTP_HEADER_CONTENT_LOCATION,
11059f82ba8fSGlenn Strauss                                          CONST_STR_LEN("Content-Location"));
11069f82ba8fSGlenn Strauss         if (vb) http_header_remap_uri(vb, 0, remap_hdrs, 0);
1107036d3d3dSGlenn Strauss     }
11089c8981a7SGlenn Strauss     if (light_btst(r->resp_htags, HTTP_HEADER_SET_COOKIE)) {
11099f82ba8fSGlenn Strauss         vb = http_header_response_get(r, HTTP_HEADER_SET_COOKIE,
11109f82ba8fSGlenn Strauss                                          CONST_STR_LEN("Set-Cookie"));
11119f82ba8fSGlenn Strauss         if (vb) http_header_remap_setcookie(vb, 0, remap_hdrs);
1112036d3d3dSGlenn Strauss     }
1113036d3d3dSGlenn Strauss 
11140a635fc8SGlenn Strauss     return HANDLER_GO_ON;
1115bcdc6a3bSJan Kneschke }
1116bcdc6a3bSJan Kneschke 
mod_proxy_check_extension(request_st * const r,void * p_d)11177c7f8c46SGlenn Strauss static handler_t mod_proxy_check_extension(request_st * const r, void *p_d) {
1118bcdc6a3bSJan Kneschke 	plugin_data *p = p_d;
111945b970e6SGlenn Strauss 	handler_t rc;
1120bcdc6a3bSJan Kneschke 
11217c7f8c46SGlenn Strauss 	if (NULL != r->handler_module) return HANDLER_GO_ON;
1122ad12e4c5SStefan Bühler 
11237c7f8c46SGlenn Strauss 	mod_proxy_patch_config(r, p);
112445b970e6SGlenn Strauss 	if (NULL == p->conf.gw.exts) return HANDLER_GO_ON;
1125bcdc6a3bSJan Kneschke 
11267c7f8c46SGlenn Strauss 	rc = gw_check_extension(r, (gw_plugin_data *)p, 1, sizeof(handler_ctx));
112745b970e6SGlenn Strauss 	if (HANDLER_GO_ON != rc) return rc;
1128bcdc6a3bSJan Kneschke 
11297c7f8c46SGlenn Strauss 	if (r->handler_module == p->self) {
11307c7f8c46SGlenn Strauss 		handler_ctx *hctx = r->plugin_ctx[p->id];
113145b970e6SGlenn Strauss 		hctx->gw.create_env = proxy_create_env;
11323d8d56d8SGlenn Strauss 		hctx->gw.response = chunk_buffer_acquire();
113345b970e6SGlenn Strauss 		hctx->gw.opts.backend = BACKEND_PROXY;
113445b970e6SGlenn Strauss 		hctx->gw.opts.pdata = hctx;
113545b970e6SGlenn Strauss 		hctx->gw.opts.headers = proxy_response_headers;
11360a635fc8SGlenn Strauss 
1137a458c2e7SGlenn Strauss 		hctx->conf = p->conf; /*(copies struct)*/
11387c7f8c46SGlenn Strauss 		hctx->conf.header.http_host = r->http_host;
11395d1aa5d0SGlenn Strauss 		hctx->conf.header.upgrade  &=
11405d1aa5d0SGlenn Strauss                   (r->http_version == HTTP_VERSION_1_1 || r->h2_connect_ext);
1141036d3d3dSGlenn Strauss 		/* mod_proxy currently sends all backend requests as http.
1142036d3d3dSGlenn Strauss 		 * https-remap is a flag since it might not be needed if backend
1143036d3d3dSGlenn Strauss 		 * honors Forwarded or X-Forwarded-Proto headers, e.g. by using
1144036d3d3dSGlenn Strauss 		 * lighttpd mod_extforward or similar functionality in backend*/
1145a458c2e7SGlenn Strauss 		if (hctx->conf.header.https_remap) {
1146a458c2e7SGlenn Strauss 			hctx->conf.header.https_remap =
11477c7f8c46SGlenn Strauss 			  buffer_is_equal_string(&r->uri.scheme, CONST_STR_LEN("https"));
1148036d3d3dSGlenn Strauss 		}
11493770df23SGlenn Strauss 
11507c7f8c46SGlenn Strauss 		if (r->http_method == HTTP_METHOD_CONNECT) {
11513770df23SGlenn Strauss 			/*(note: not requiring HTTP/1.1 due to too many non-compliant
11523770df23SGlenn Strauss 			 * clients such as 'openssl s_client')*/
11535d1aa5d0SGlenn Strauss 			if (r->h2_connect_ext
11545d1aa5d0SGlenn Strauss 			    && (hctx->conf.header.connect_method =
11555d1aa5d0SGlenn Strauss 			          hctx->conf.header.upgrade)) { /*(405 if not set)*/
11565d1aa5d0SGlenn Strauss 				/*(not bothering to check (!hctx->conf.header.force_http10))*/
11575d1aa5d0SGlenn Strauss 				/*hctx->gw.create_env = proxy_create_env;*/ /*(preserve)*/
11585d1aa5d0SGlenn Strauss 			}
11595d1aa5d0SGlenn Strauss 			else if (hctx->conf.header.connect_method) {
11603770df23SGlenn Strauss 				hctx->gw.create_env = proxy_create_env_connect;
11613770df23SGlenn Strauss 			}
11623770df23SGlenn Strauss 			else {
11637c7f8c46SGlenn Strauss 				r->http_status = 405; /* Method Not Allowed */
11647c7f8c46SGlenn Strauss 				r->handler_module = NULL;
11653770df23SGlenn Strauss 				return HANDLER_FINISHED;
11663770df23SGlenn Strauss 			}
11673770df23SGlenn Strauss 		}
1168bca076c9SJan Kneschke 	}
1169bca076c9SJan Kneschke 
1170bca076c9SJan Kneschke 	return HANDLER_GO_ON;
1171bca076c9SJan Kneschke }
1172bca076c9SJan Kneschke 
1173bca076c9SJan Kneschke 
1174b82d7b8aSGlenn Strauss __attribute_cold__
117563f785a2SStefan Bühler int mod_proxy_plugin_init(plugin *p);
mod_proxy_plugin_init(plugin * p)1176bcdc6a3bSJan Kneschke int mod_proxy_plugin_init(plugin *p) {
1177bcdc6a3bSJan Kneschke 	p->version      = LIGHTTPD_VERSION_ID;
1178e2de4e58SGlenn Strauss 	p->name         = "proxy";
1179bcdc6a3bSJan Kneschke 
1180bcdc6a3bSJan Kneschke 	p->init         = mod_proxy_init;
1181bcdc6a3bSJan Kneschke 	p->cleanup      = mod_proxy_free;
1182bcdc6a3bSJan Kneschke 	p->set_defaults = mod_proxy_set_defaults;
118333c8cf41SGlenn Strauss 	p->handle_request_reset    = gw_handle_request_reset;
1184bcdc6a3bSJan Kneschke 	p->handle_uri_clean        = mod_proxy_check_extension;
118545b970e6SGlenn Strauss 	p->handle_subrequest       = gw_handle_subrequest;
118645b970e6SGlenn Strauss 	p->handle_trigger          = gw_handle_trigger;
11879030cfaeSGlenn Strauss 	p->handle_waitpid          = gw_handle_waitpid_cb;
1188bcdc6a3bSJan Kneschke 
1189bcdc6a3bSJan Kneschke 	return 0;
1190bcdc6a3bSJan Kneschke }
1191