lighttpd1.4/src/mod_proxy.c

1134 lines
43 KiB
C
Raw Normal View History

#include "first.h"
[core] shared code for socket backends common codebase for socket backends, based off mod_fastcgi with some features added for mod_proxy (mostly intended to reduce code duplication and enhance code isolation) mod_fastcgi and mod_scgi can now use fastcgi.balance and scgi.balance for similar behavior as proxy.balance, but the balancing is per-host and not per-proc. proxy.balance is also per-host and not per-proc. mod_proxy and mod_scgi can now use proxy.map-extensions and scgi.map-extensions, similar to fastcgi.map-extensions. mod_fastcgi behavior change (affects only mod_status): - statistics tags have been renamed from "fastcgi.*" to "gw.*" "fastcgi.backend.*" -> "gw.backend.*" "fastcgi.active-requests" -> "gw.active-requests" ("fastcgi.requests" remains "fastcgi.requests") ("proxy.requests" is new) ("scgi.requests" is new) mod_scgi behavior change (likely minor): - removed scgi_proclist_sort_down() and scgi_proclist_sort_up(). procs now chosen based on load as measured by num socket connnections Note: modules using gw_backend.[ch] are currently still independent modules. If it had been written as a single module with fastcgi, scgi, proxy implementations, then there would have been a chance of breaking some existing user configurations where module ordering made a difference for which module handled a given request, though for most people, this would have made no difference. Details about mod_fastcgi code transformations: unsigned int debug -> int debug fastcgi_env member removed from plugin_config renamed "fcgi" and "fastcgi" to "gw", and "FCGI" to "GW" reorganize routines for high-level and lower-level interfaces some lower-level internal interfaces changed to use host,proc,debug args rather than knowing about higher-level (app) hctx and plugin_data tabs->spaces and reformatting
2017-07-14 05:29:18 +00:00
#include <string.h>
#include <stdlib.h>
#include "gw_backend.h"
#include "base.h"
#include "array.h"
#include "buffer.h"
#include "fdevent.h"
#include "http_kv.h"
#include "http_header.h"
#include "log.h"
2017-10-29 05:23:19 +00:00
#include "sock_addr.h"
[core] shared code for socket backends common codebase for socket backends, based off mod_fastcgi with some features added for mod_proxy (mostly intended to reduce code duplication and enhance code isolation) mod_fastcgi and mod_scgi can now use fastcgi.balance and scgi.balance for similar behavior as proxy.balance, but the balancing is per-host and not per-proc. proxy.balance is also per-host and not per-proc. mod_proxy and mod_scgi can now use proxy.map-extensions and scgi.map-extensions, similar to fastcgi.map-extensions. mod_fastcgi behavior change (affects only mod_status): - statistics tags have been renamed from "fastcgi.*" to "gw.*" "fastcgi.backend.*" -> "gw.backend.*" "fastcgi.active-requests" -> "gw.active-requests" ("fastcgi.requests" remains "fastcgi.requests") ("proxy.requests" is new) ("scgi.requests" is new) mod_scgi behavior change (likely minor): - removed scgi_proclist_sort_down() and scgi_proclist_sort_up(). procs now chosen based on load as measured by num socket connnections Note: modules using gw_backend.[ch] are currently still independent modules. If it had been written as a single module with fastcgi, scgi, proxy implementations, then there would have been a chance of breaking some existing user configurations where module ordering made a difference for which module handled a given request, though for most people, this would have made no difference. Details about mod_fastcgi code transformations: unsigned int debug -> int debug fastcgi_env member removed from plugin_config renamed "fcgi" and "fastcgi" to "gw", and "FCGI" to "GW" reorganize routines for high-level and lower-level interfaces some lower-level internal interfaces changed to use host,proc,debug args rather than knowing about higher-level (app) hctx and plugin_data tabs->spaces and reformatting
2017-07-14 05:29:18 +00:00
#include "status_counter.h"
/**
*
* HTTP reverse proxy
*
* TODO: - HTTP/1.1
* - HTTP/1.1 persistent connection with upstream servers
*/
[mod_proxy] simple host/url mapping in headers (fixes #152) Provide a simple mechanism for mapping host and urlpath header strings in proxied request and response well-known headers. This *is not* intended as a one-size-fits-all, infinitely extensible, regex rewriting engine. Instead, the proxy.header directive aims to provide built-in functionality in mod_proxy for a few common use cases by performing simple host matching or urlpath prefix matching, and using the mapping of the first match. More complex use cases could possibly be handled by a custom lighttpd module (which does not currently exist). Note: the contents of the HTTP request-line and HTTP headers may or may not be in normalized canonical forms, which may or may not influence the simple matching performed. Admins should take care to provide safe defaults (fail closed) if mapping is expected to occur and blindly passing non-mapped requests is undesirable. proxy.header = ( #"map-host-request" => ( #"-" => "...",#replace provided given Host request authority #"..." => "-",#preserve existing authority (no further matching) #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-host-response" => ( #"-" => "...",#replace authority used in backend request #"..." => "-",#replace with original authority #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-urlpath" => ( #"/xxx" => "/yyy",#map one urlpath prefix to another #"/xxx/" => "/", #map one urlpath prefix to another #"/xxx" => "", #map one urlpath prefix to another #"/key" => "/value", # Note: request headers have matching "key" prefix replaced with # "value", and response headers have matching "value" prefix # replaced with "key", with a pre-test of the "value" from the # first-matched "key" in request headers (if there was a match) #), #"https-remap" => "enable", # For https requests from client, map https:// to http:// # when map-host-request matches URI in request, and map http:// # to https:// when map-host-response matches URI in response. # (mod_proxy currently sends all backend requests as http) ) x-ref: "feature to remove part of the URI when passing along requests..." https://redmine.lighttpd.net/issues/152
2017-04-28 22:53:08 +00:00
/* (future: might split struct and move part to http-header-glue.c) */
typedef struct http_header_remap_opts {
const array *urlpaths;
const array *hosts_request;
const array *hosts_response;
int https_remap;
int upgrade;
int connect_method;
[mod_proxy] simple host/url mapping in headers (fixes #152) Provide a simple mechanism for mapping host and urlpath header strings in proxied request and response well-known headers. This *is not* intended as a one-size-fits-all, infinitely extensible, regex rewriting engine. Instead, the proxy.header directive aims to provide built-in functionality in mod_proxy for a few common use cases by performing simple host matching or urlpath prefix matching, and using the mapping of the first match. More complex use cases could possibly be handled by a custom lighttpd module (which does not currently exist). Note: the contents of the HTTP request-line and HTTP headers may or may not be in normalized canonical forms, which may or may not influence the simple matching performed. Admins should take care to provide safe defaults (fail closed) if mapping is expected to occur and blindly passing non-mapped requests is undesirable. proxy.header = ( #"map-host-request" => ( #"-" => "...",#replace provided given Host request authority #"..." => "-",#preserve existing authority (no further matching) #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-host-response" => ( #"-" => "...",#replace authority used in backend request #"..." => "-",#replace with original authority #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-urlpath" => ( #"/xxx" => "/yyy",#map one urlpath prefix to another #"/xxx/" => "/", #map one urlpath prefix to another #"/xxx" => "", #map one urlpath prefix to another #"/key" => "/value", # Note: request headers have matching "key" prefix replaced with # "value", and response headers have matching "value" prefix # replaced with "key", with a pre-test of the "value" from the # first-matched "key" in request headers (if there was a match) #), #"https-remap" => "enable", # For https requests from client, map https:// to http:// # when map-host-request matches URI in request, and map http:// # to https:// when map-host-response matches URI in response. # (mod_proxy currently sends all backend requests as http) ) x-ref: "feature to remove part of the URI when passing along requests..." https://redmine.lighttpd.net/issues/152
2017-04-28 22:53:08 +00:00
/*(not used in plugin_config, but used in handler_ctx)*/
const buffer *http_host;
const buffer *forwarded_host;
const data_string *forwarded_urlpath;
} http_header_remap_opts;
typedef enum {
PROXY_FORWARDED_NONE = 0x00,
PROXY_FORWARDED_FOR = 0x01,
PROXY_FORWARDED_PROTO = 0x02,
PROXY_FORWARDED_HOST = 0x04,
PROXY_FORWARDED_BY = 0x08,
PROXY_FORWARDED_REMOTE_USER = 0x10
} proxy_forwarded_t;
typedef struct {
gw_plugin_config gw; /* start must match layout of gw_plugin_config */
unsigned int replace_http_host;
unsigned int forwarded;
http_header_remap_opts header;
} plugin_config;
typedef struct {
PLUGIN_DATA;
pid_t srv_pid; /* must match layout of gw_plugin_data through conf member */
plugin_config conf;
plugin_config defaults;
} plugin_data;
static int proxy_check_extforward;
typedef struct {
[core] shared code for socket backends common codebase for socket backends, based off mod_fastcgi with some features added for mod_proxy (mostly intended to reduce code duplication and enhance code isolation) mod_fastcgi and mod_scgi can now use fastcgi.balance and scgi.balance for similar behavior as proxy.balance, but the balancing is per-host and not per-proc. proxy.balance is also per-host and not per-proc. mod_proxy and mod_scgi can now use proxy.map-extensions and scgi.map-extensions, similar to fastcgi.map-extensions. mod_fastcgi behavior change (affects only mod_status): - statistics tags have been renamed from "fastcgi.*" to "gw.*" "fastcgi.backend.*" -> "gw.backend.*" "fastcgi.active-requests" -> "gw.active-requests" ("fastcgi.requests" remains "fastcgi.requests") ("proxy.requests" is new) ("scgi.requests" is new) mod_scgi behavior change (likely minor): - removed scgi_proclist_sort_down() and scgi_proclist_sort_up(). procs now chosen based on load as measured by num socket connnections Note: modules using gw_backend.[ch] are currently still independent modules. If it had been written as a single module with fastcgi, scgi, proxy implementations, then there would have been a chance of breaking some existing user configurations where module ordering made a difference for which module handled a given request, though for most people, this would have made no difference. Details about mod_fastcgi code transformations: unsigned int debug -> int debug fastcgi_env member removed from plugin_config renamed "fcgi" and "fastcgi" to "gw", and "FCGI" to "GW" reorganize routines for high-level and lower-level interfaces some lower-level internal interfaces changed to use host,proc,debug args rather than knowing about higher-level (app) hctx and plugin_data tabs->spaces and reformatting
2017-07-14 05:29:18 +00:00
gw_handler_ctx gw;
http_response_opts opts;
plugin_config conf;
} handler_ctx;
INIT_FUNC(mod_proxy_init) {
return calloc(1, sizeof(plugin_data));
}
static void mod_proxy_free_config(plugin_data * const p)
{
if (NULL == p->cvlist) return;
/* (init i to 0 if global context; to 1 to skip empty global context) */
for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) {
config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
for (; -1 != cpv->k_id; ++cpv) {
switch (cpv->k_id) {
case 5: /* proxy.header */
if (cpv->vtype == T_CONFIG_LOCAL) free(cpv->v.v);
break;
default:
break;
}
}
}
}
FREE_FUNC(mod_proxy_free) {
plugin_data * const p = p_d;
mod_proxy_free_config(p);
gw_free(p);
}
static void mod_proxy_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv)
{
switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */
case 0: /* proxy.server */
if (cpv->vtype == T_CONFIG_LOCAL) {
gw_plugin_config * const gw = cpv->v.v;
pconf->gw.exts = gw->exts;
pconf->gw.exts_auth = gw->exts_auth;
pconf->gw.exts_resp = gw->exts_resp;
}
break;
case 1: /* proxy.balance */
/*if (cpv->vtype == T_CONFIG_LOCAL)*//*always true here for this param*/
pconf->gw.balance = (int)cpv->v.u;
break;
case 2: /* proxy.debug */
pconf->gw.debug = (int)cpv->v.u;
break;
case 3: /* proxy.map-extensions */
pconf->gw.ext_mapping = cpv->v.a;
break;
case 4: /* proxy.forwarded */
/*if (cpv->vtype == T_CONFIG_LOCAL)*//*always true here for this param*/
pconf->forwarded = cpv->v.u;
break;
case 5: /* proxy.header */
/*if (cpv->vtype == T_CONFIG_LOCAL)*//*always true here for this param*/
pconf->header = *(http_header_remap_opts *)cpv->v.v; /*(copies struct)*/
break;
case 6: /* proxy.replace-http-host */
pconf->replace_http_host = cpv->v.u;
break;
default:/* should not happen */
return;
}
}
static void mod_proxy_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv)
{
do {
mod_proxy_merge_config_cpv(pconf, cpv);
} while ((++cpv)->k_id != -1);
}
static void mod_proxy_patch_config(request_st * const r, plugin_data * const p)
{
memcpy(&p->conf, &p->defaults, sizeof(plugin_config));
for (int i = 1, used = p->nconfig; i < used; ++i) {
if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id))
mod_proxy_merge_config(&p->conf, p->cvlist+p->cvlist[i].v.u2[0]);
}
}
static unsigned int mod_proxy_parse_forwarded(server *srv, const array *a)
{
unsigned int forwarded = 0;
for (uint32_t j = 0, used = a->used; j < used; ++j) {
proxy_forwarded_t param;
data_unset *du = a->data[j];
if (buffer_eq_slen(&du->key, CONST_STR_LEN("by")))
param = PROXY_FORWARDED_BY;
else if (buffer_eq_slen(&du->key, CONST_STR_LEN("for")))
param = PROXY_FORWARDED_FOR;
else if (buffer_eq_slen(&du->key, CONST_STR_LEN("host")))
param = PROXY_FORWARDED_HOST;
else if (buffer_eq_slen(&du->key, CONST_STR_LEN("proto")))
param = PROXY_FORWARDED_PROTO;
else if (buffer_eq_slen(&du->key, CONST_STR_LEN("remote_user")))
param = PROXY_FORWARDED_REMOTE_USER;
else {
log_error(srv->errh, __FILE__, __LINE__,
"proxy.forwarded keys must be one of: "
"by, for, host, proto, remote_user, but not: %s", du->key.ptr);
return UINT_MAX;
}
if (du->type == TYPE_STRING) {
data_string *ds = (data_string *)du;
if (buffer_eq_slen(&ds->value, CONST_STR_LEN("enable")))
forwarded |= param;
else if (!buffer_eq_slen(&ds->value, CONST_STR_LEN("disable"))) {
log_error(srv->errh, __FILE__, __LINE__,
"proxy.forwarded values must be one of: "
"0, 1, enable, disable; error for key: %s", du->key.ptr);
return UINT_MAX;
}
}
else if (du->type == TYPE_INTEGER) {
data_integer *di = (data_integer *)du;
if (di->value) forwarded |= param;
}
else {
log_error(srv->errh, __FILE__, __LINE__,
"proxy.forwarded values must be one of: "
"0, 1, enable, disable; error for key: %s", du->key.ptr);
return UINT_MAX;
}
}
return forwarded;
}
[core] shared code for socket backends common codebase for socket backends, based off mod_fastcgi with some features added for mod_proxy (mostly intended to reduce code duplication and enhance code isolation) mod_fastcgi and mod_scgi can now use fastcgi.balance and scgi.balance for similar behavior as proxy.balance, but the balancing is per-host and not per-proc. proxy.balance is also per-host and not per-proc. mod_proxy and mod_scgi can now use proxy.map-extensions and scgi.map-extensions, similar to fastcgi.map-extensions. mod_fastcgi behavior change (affects only mod_status): - statistics tags have been renamed from "fastcgi.*" to "gw.*" "fastcgi.backend.*" -> "gw.backend.*" "fastcgi.active-requests" -> "gw.active-requests" ("fastcgi.requests" remains "fastcgi.requests") ("proxy.requests" is new) ("scgi.requests" is new) mod_scgi behavior change (likely minor): - removed scgi_proclist_sort_down() and scgi_proclist_sort_up(). procs now chosen based on load as measured by num socket connnections Note: modules using gw_backend.[ch] are currently still independent modules. If it had been written as a single module with fastcgi, scgi, proxy implementations, then there would have been a chance of breaking some existing user configurations where module ordering made a difference for which module handled a given request, though for most people, this would have made no difference. Details about mod_fastcgi code transformations: unsigned int debug -> int debug fastcgi_env member removed from plugin_config renamed "fcgi" and "fastcgi" to "gw", and "FCGI" to "GW" reorganize routines for high-level and lower-level interfaces some lower-level internal interfaces changed to use host,proc,debug args rather than knowing about higher-level (app) hctx and plugin_data tabs->spaces and reformatting
2017-07-14 05:29:18 +00:00
static http_header_remap_opts * mod_proxy_parse_header_opts(server *srv, const array *a)
{
http_header_remap_opts header;
memset(&header, 0, sizeof(header));
for (uint32_t j = 0, used = a->used; j < used; ++j) {
data_array *da = (data_array *)a->data[j];
if (buffer_eq_slen(&da->key, CONST_STR_LEN("https-remap"))) {
data_string *ds = (data_string *)da;
if (ds->type != TYPE_STRING) {
log_error(srv->errh, __FILE__, __LINE__,
"unexpected value for proxy.header; "
"expected \"enable\" or \"disable\" for https-remap");
return NULL;
}
header.https_remap =
!buffer_eq_slen(&ds->value, CONST_STR_LEN("disable"))
&& !buffer_eq_slen(&ds->value, CONST_STR_LEN("0"));
continue;
}
else if (buffer_eq_slen(&da->key, CONST_STR_LEN("upgrade"))) {
data_string *ds = (data_string *)da;
if (ds->type != TYPE_STRING) {
log_error(srv->errh, __FILE__, __LINE__,
"unexpected value for proxy.header; "
"expected \"upgrade\" => \"enable\" or \"disable\"");
return NULL;
}
header.upgrade =
!buffer_eq_slen(&ds->value, CONST_STR_LEN("disable"))
&& !buffer_eq_slen(&ds->value, CONST_STR_LEN("0"));
continue;
}
else if (buffer_eq_slen(&da->key, CONST_STR_LEN("connect"))) {
data_string *ds = (data_string *)da;
if (ds->type != TYPE_STRING) {
log_error(srv->errh, __FILE__, __LINE__,
"unexpected value for proxy.header; "
"expected \"connect\" => \"enable\" or \"disable\"");
return NULL;
}
header.connect_method =
!buffer_eq_slen(&ds->value, CONST_STR_LEN("disable"))
&& !buffer_eq_slen(&ds->value, CONST_STR_LEN("0"));
continue;
}
if (da->type != TYPE_ARRAY || !array_is_kvstring(&da->value)) {
log_error(srv->errh, __FILE__, __LINE__,
"unexpected value for proxy.header; "
"expected ( \"param\" => ( \"key\" => \"value\" ) ) near key %s",
da->key.ptr);
return NULL;
}
if (buffer_eq_slen(&da->key, CONST_STR_LEN("map-urlpath"))) {
header.urlpaths = &da->value;
}
else if (buffer_eq_slen(&da->key, CONST_STR_LEN("map-host-request"))) {
header.hosts_request = &da->value;
}
else if (buffer_eq_slen(&da->key, CONST_STR_LEN("map-host-response"))) {
header.hosts_response = &da->value;
}
else {
log_error(srv->errh, __FILE__, __LINE__,
"unexpected key for proxy.header; "
"expected ( \"param\" => ( \"key\" => \"value\" ) ) near key %s",
da->key.ptr);
return NULL;
}
}
[core] shared code for socket backends common codebase for socket backends, based off mod_fastcgi with some features added for mod_proxy (mostly intended to reduce code duplication and enhance code isolation) mod_fastcgi and mod_scgi can now use fastcgi.balance and scgi.balance for similar behavior as proxy.balance, but the balancing is per-host and not per-proc. proxy.balance is also per-host and not per-proc. mod_proxy and mod_scgi can now use proxy.map-extensions and scgi.map-extensions, similar to fastcgi.map-extensions. mod_fastcgi behavior change (affects only mod_status): - statistics tags have been renamed from "fastcgi.*" to "gw.*" "fastcgi.backend.*" -> "gw.backend.*" "fastcgi.active-requests" -> "gw.active-requests" ("fastcgi.requests" remains "fastcgi.requests") ("proxy.requests" is new) ("scgi.requests" is new) mod_scgi behavior change (likely minor): - removed scgi_proclist_sort_down() and scgi_proclist_sort_up(). procs now chosen based on load as measured by num socket connnections Note: modules using gw_backend.[ch] are currently still independent modules. If it had been written as a single module with fastcgi, scgi, proxy implementations, then there would have been a chance of breaking some existing user configurations where module ordering made a difference for which module handled a given request, though for most people, this would have made no difference. Details about mod_fastcgi code transformations: unsigned int debug -> int debug fastcgi_env member removed from plugin_config renamed "fcgi" and "fastcgi" to "gw", and "FCGI" to "GW" reorganize routines for high-level and lower-level interfaces some lower-level internal interfaces changed to use host,proc,debug args rather than knowing about higher-level (app) hctx and plugin_data tabs->spaces and reformatting
2017-07-14 05:29:18 +00:00
http_header_remap_opts *opts = malloc(sizeof(header));
force_assert(opts);
memcpy(opts, &header, sizeof(header));
return opts;
}
SETDEFAULTS_FUNC(mod_proxy_set_defaults)
{
static const config_plugin_keys_t cpk[] = {
{ CONST_STR_LEN("proxy.server"),
T_CONFIG_ARRAY_KVARRAY,
T_CONFIG_SCOPE_CONNECTION }
,{ CONST_STR_LEN("proxy.balance"),
T_CONFIG_STRING,
T_CONFIG_SCOPE_CONNECTION }
,{ CONST_STR_LEN("proxy.debug"),
T_CONFIG_INT,
T_CONFIG_SCOPE_CONNECTION }
,{ CONST_STR_LEN("proxy.map-extensions"),
T_CONFIG_ARRAY_KVSTRING,
T_CONFIG_SCOPE_CONNECTION }
,{ CONST_STR_LEN("proxy.forwarded"),
T_CONFIG_ARRAY_KVANY,
T_CONFIG_SCOPE_CONNECTION }
,{ CONST_STR_LEN("proxy.header"),
T_CONFIG_ARRAY_KVANY,
T_CONFIG_SCOPE_CONNECTION }
,{ CONST_STR_LEN("proxy.replace-http-host"),
T_CONFIG_BOOL,
T_CONFIG_SCOPE_CONNECTION }
,{ NULL, 0,
T_CONFIG_UNSET,
T_CONFIG_SCOPE_UNSET }
};
plugin_data * const p = p_d;
if (!config_plugin_values_init(srv, p, cpk, "mod_proxy"))
return HANDLER_ERROR;
/* process and validate config directives
* (init i to 0 if global context; to 1 to skip empty global context) */
for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
gw_plugin_config *gw = NULL;
for (; -1 != cpv->k_id; ++cpv) {
switch (cpv->k_id) {
case 0: /* proxy.server */
gw = calloc(1, sizeof(gw_plugin_config));
force_assert(gw);
if (!gw_set_defaults_backend(srv, (gw_plugin_data *)p, cpv->v.a,
gw, 0, cpk[cpv->k_id].k)) {
gw_plugin_config_free(gw);
return HANDLER_ERROR;
}
/* error if "mode" = "authorizer";
* proxy can not act as authorizer */
/*(check after gw_set_defaults_backend())*/
if (gw->exts_auth && gw->exts_auth->used) {
log_error(srv->errh, __FILE__, __LINE__,
"%s must not define any hosts with "
"attribute \"mode\" = \"authorizer\"", cpk[cpv->k_id].k);
gw_plugin_config_free(gw);
return HANDLER_ERROR;
}
cpv->v.v = gw;
cpv->vtype = T_CONFIG_LOCAL;
break;
case 1: /* proxy.balance */
cpv->v.u = (unsigned int)gw_get_defaults_balance(srv, cpv->v.b);
break;
case 2: /* proxy.debug */
case 3: /* proxy.map-extensions */
break;
case 4: /* proxy.forwarded */
cpv->v.u = mod_proxy_parse_forwarded(srv, cpv->v.a);
if (UINT_MAX == cpv->v.u) return HANDLER_ERROR;
cpv->vtype = T_CONFIG_LOCAL;
break;
case 5: /* proxy.header */
cpv->v.v = mod_proxy_parse_header_opts(srv, cpv->v.a);
if (NULL == cpv->v.v) return HANDLER_ERROR;
cpv->vtype = T_CONFIG_LOCAL;
break;
case 6: /* proxy.replace-http-host */
break;
default:/* should not happen */
break;
}
}
/* disable check-local for all exts (default enabled) */
if (gw && gw->exts) { /*(check after gw_set_defaults_backend())*/
2019-11-23 04:13:12 +00:00
gw_exts_clear_check_local(gw->exts);
}
}
/* default is 0 */
/*p->defaults.balance = (unsigned int)gw_get_defaults_balance(srv, NULL);*/
/* initialize p->defaults from global config context */
if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
if (-1 != cpv->k_id)
mod_proxy_merge_config(&p->defaults, cpv);
}
/* special-case behavior if mod_extforward is loaded */
for (uint32_t i = 0; i < srv->srvconf.modules->used; ++i) {
buffer *m = &((data_string *)srv->srvconf.modules->data[i])->value;
if (buffer_eq_slen(m, CONST_STR_LEN("mod_extforward"))) {
proxy_check_extforward = 1;
break;
}
}
return HANDLER_GO_ON;
}
[mod_proxy] simple host/url mapping in headers (fixes #152) Provide a simple mechanism for mapping host and urlpath header strings in proxied request and response well-known headers. This *is not* intended as a one-size-fits-all, infinitely extensible, regex rewriting engine. Instead, the proxy.header directive aims to provide built-in functionality in mod_proxy for a few common use cases by performing simple host matching or urlpath prefix matching, and using the mapping of the first match. More complex use cases could possibly be handled by a custom lighttpd module (which does not currently exist). Note: the contents of the HTTP request-line and HTTP headers may or may not be in normalized canonical forms, which may or may not influence the simple matching performed. Admins should take care to provide safe defaults (fail closed) if mapping is expected to occur and blindly passing non-mapped requests is undesirable. proxy.header = ( #"map-host-request" => ( #"-" => "...",#replace provided given Host request authority #"..." => "-",#preserve existing authority (no further matching) #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-host-response" => ( #"-" => "...",#replace authority used in backend request #"..." => "-",#replace with original authority #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-urlpath" => ( #"/xxx" => "/yyy",#map one urlpath prefix to another #"/xxx/" => "/", #map one urlpath prefix to another #"/xxx" => "", #map one urlpath prefix to another #"/key" => "/value", # Note: request headers have matching "key" prefix replaced with # "value", and response headers have matching "value" prefix # replaced with "key", with a pre-test of the "value" from the # first-matched "key" in request headers (if there was a match) #), #"https-remap" => "enable", # For https requests from client, map https:// to http:// # when map-host-request matches URI in request, and map http:// # to https:// when map-host-response matches URI in response. # (mod_proxy currently sends all backend requests as http) ) x-ref: "feature to remove part of the URI when passing along requests..." https://redmine.lighttpd.net/issues/152
2017-04-28 22:53:08 +00:00
/* (future: might move to http-header-glue.c) */
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)
{
const array *hosts = is_req
? remap_hdrs->hosts_request
: remap_hdrs->hosts_response;
if (hosts) {
const char * const s = b->ptr+off;
for (size_t i = 0, used = hosts->used; i < used; ++i) {
const data_string * const ds = (data_string *)hosts->data[i];
const buffer *k = &ds->key;
[mod_proxy] simple host/url mapping in headers (fixes #152) Provide a simple mechanism for mapping host and urlpath header strings in proxied request and response well-known headers. This *is not* intended as a one-size-fits-all, infinitely extensible, regex rewriting engine. Instead, the proxy.header directive aims to provide built-in functionality in mod_proxy for a few common use cases by performing simple host matching or urlpath prefix matching, and using the mapping of the first match. More complex use cases could possibly be handled by a custom lighttpd module (which does not currently exist). Note: the contents of the HTTP request-line and HTTP headers may or may not be in normalized canonical forms, which may or may not influence the simple matching performed. Admins should take care to provide safe defaults (fail closed) if mapping is expected to occur and blindly passing non-mapped requests is undesirable. proxy.header = ( #"map-host-request" => ( #"-" => "...",#replace provided given Host request authority #"..." => "-",#preserve existing authority (no further matching) #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-host-response" => ( #"-" => "...",#replace authority used in backend request #"..." => "-",#replace with original authority #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-urlpath" => ( #"/xxx" => "/yyy",#map one urlpath prefix to another #"/xxx/" => "/", #map one urlpath prefix to another #"/xxx" => "", #map one urlpath prefix to another #"/key" => "/value", # Note: request headers have matching "key" prefix replaced with # "value", and response headers have matching "value" prefix # replaced with "key", with a pre-test of the "value" from the # first-matched "key" in request headers (if there was a match) #), #"https-remap" => "enable", # For https requests from client, map https:// to http:// # when map-host-request matches URI in request, and map http:// # to https:// when map-host-response matches URI in response. # (mod_proxy currently sends all backend requests as http) ) x-ref: "feature to remove part of the URI when passing along requests..." https://redmine.lighttpd.net/issues/152
2017-04-28 22:53:08 +00:00
size_t mlen = buffer_string_length(k);
if (1 == mlen && k->ptr[0] == '-') {
/* match with authority provided in Host (if is_req)
* (If no Host in client request, then matching against empty
* string will probably not match, and no remap will be
* performed) */
k = is_req
? remap_hdrs->http_host
: remap_hdrs->forwarded_host;
if (NULL == k) continue;
mlen = buffer_string_length(k);
}
if (buffer_eq_icase_ss(s, alen, k->ptr, mlen)) {
if (buffer_is_equal_string(&ds->value, CONST_STR_LEN("-"))) {
[mod_proxy] simple host/url mapping in headers (fixes #152) Provide a simple mechanism for mapping host and urlpath header strings in proxied request and response well-known headers. This *is not* intended as a one-size-fits-all, infinitely extensible, regex rewriting engine. Instead, the proxy.header directive aims to provide built-in functionality in mod_proxy for a few common use cases by performing simple host matching or urlpath prefix matching, and using the mapping of the first match. More complex use cases could possibly be handled by a custom lighttpd module (which does not currently exist). Note: the contents of the HTTP request-line and HTTP headers may or may not be in normalized canonical forms, which may or may not influence the simple matching performed. Admins should take care to provide safe defaults (fail closed) if mapping is expected to occur and blindly passing non-mapped requests is undesirable. proxy.header = ( #"map-host-request" => ( #"-" => "...",#replace provided given Host request authority #"..." => "-",#preserve existing authority (no further matching) #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-host-response" => ( #"-" => "...",#replace authority used in backend request #"..." => "-",#replace with original authority #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-urlpath" => ( #"/xxx" => "/yyy",#map one urlpath prefix to another #"/xxx/" => "/", #map one urlpath prefix to another #"/xxx" => "", #map one urlpath prefix to another #"/key" => "/value", # Note: request headers have matching "key" prefix replaced with # "value", and response headers have matching "value" prefix # replaced with "key", with a pre-test of the "value" from the # first-matched "key" in request headers (if there was a match) #), #"https-remap" => "enable", # For https requests from client, map https:// to http:// # when map-host-request matches URI in request, and map http:// # to https:// when map-host-response matches URI in response. # (mod_proxy currently sends all backend requests as http) ) x-ref: "feature to remove part of the URI when passing along requests..." https://redmine.lighttpd.net/issues/152
2017-04-28 22:53:08 +00:00
return remap_hdrs->http_host;
}
else if (!buffer_string_is_empty(&ds->value)) {
[mod_proxy] simple host/url mapping in headers (fixes #152) Provide a simple mechanism for mapping host and urlpath header strings in proxied request and response well-known headers. This *is not* intended as a one-size-fits-all, infinitely extensible, regex rewriting engine. Instead, the proxy.header directive aims to provide built-in functionality in mod_proxy for a few common use cases by performing simple host matching or urlpath prefix matching, and using the mapping of the first match. More complex use cases could possibly be handled by a custom lighttpd module (which does not currently exist). Note: the contents of the HTTP request-line and HTTP headers may or may not be in normalized canonical forms, which may or may not influence the simple matching performed. Admins should take care to provide safe defaults (fail closed) if mapping is expected to occur and blindly passing non-mapped requests is undesirable. proxy.header = ( #"map-host-request" => ( #"-" => "...",#replace provided given Host request authority #"..." => "-",#preserve existing authority (no further matching) #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-host-response" => ( #"-" => "...",#replace authority used in backend request #"..." => "-",#replace with original authority #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-urlpath" => ( #"/xxx" => "/yyy",#map one urlpath prefix to another #"/xxx/" => "/", #map one urlpath prefix to another #"/xxx" => "", #map one urlpath prefix to another #"/key" => "/value", # Note: request headers have matching "key" prefix replaced with # "value", and response headers have matching "value" prefix # replaced with "key", with a pre-test of the "value" from the # first-matched "key" in request headers (if there was a match) #), #"https-remap" => "enable", # For https requests from client, map https:// to http:// # when map-host-request matches URI in request, and map http:// # to https:// when map-host-response matches URI in response. # (mod_proxy currently sends all backend requests as http) ) x-ref: "feature to remove part of the URI when passing along requests..." https://redmine.lighttpd.net/issues/152
2017-04-28 22:53:08 +00:00
/*(save first matched request host for response match)*/
if (is_req && NULL == remap_hdrs->forwarded_host)
remap_hdrs->forwarded_host = &ds->value;
return &ds->value;
[mod_proxy] simple host/url mapping in headers (fixes #152) Provide a simple mechanism for mapping host and urlpath header strings in proxied request and response well-known headers. This *is not* intended as a one-size-fits-all, infinitely extensible, regex rewriting engine. Instead, the proxy.header directive aims to provide built-in functionality in mod_proxy for a few common use cases by performing simple host matching or urlpath prefix matching, and using the mapping of the first match. More complex use cases could possibly be handled by a custom lighttpd module (which does not currently exist). Note: the contents of the HTTP request-line and HTTP headers may or may not be in normalized canonical forms, which may or may not influence the simple matching performed. Admins should take care to provide safe defaults (fail closed) if mapping is expected to occur and blindly passing non-mapped requests is undesirable. proxy.header = ( #"map-host-request" => ( #"-" => "...",#replace provided given Host request authority #"..." => "-",#preserve existing authority (no further matching) #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-host-response" => ( #"-" => "...",#replace authority used in backend request #"..." => "-",#replace with original authority #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-urlpath" => ( #"/xxx" => "/yyy",#map one urlpath prefix to another #"/xxx/" => "/", #map one urlpath prefix to another #"/xxx" => "", #map one urlpath prefix to another #"/key" => "/value", # Note: request headers have matching "key" prefix replaced with # "value", and response headers have matching "value" prefix # replaced with "key", with a pre-test of the "value" from the # first-matched "key" in request headers (if there was a match) #), #"https-remap" => "enable", # For https requests from client, map https:// to http:// # when map-host-request matches URI in request, and map http:// # to https:// when map-host-response matches URI in response. # (mod_proxy currently sends all backend requests as http) ) x-ref: "feature to remove part of the URI when passing along requests..." https://redmine.lighttpd.net/issues/152
2017-04-28 22:53:08 +00:00
} /*(else leave authority as-is and stop matching)*/
break;
}
}
}
return NULL;
}
/* (future: might move to http-header-glue.c) */
static size_t http_header_remap_host (buffer *b, size_t off, http_header_remap_opts *remap_hdrs, int is_req, size_t alen)
{
const buffer * const m =
http_header_remap_host_match(b, off, remap_hdrs, is_req, alen);
if (NULL == m) return alen; /*(no match; return original authority length)*/
buffer_substr_replace(b, off, alen, m);
return buffer_string_length(m); /*(length of replacement authority)*/
}
/* (future: might move to http-header-glue.c) */
static size_t http_header_remap_urlpath (buffer *b, size_t off, http_header_remap_opts *remap_hdrs, int is_req)
[mod_proxy] simple host/url mapping in headers (fixes #152) Provide a simple mechanism for mapping host and urlpath header strings in proxied request and response well-known headers. This *is not* intended as a one-size-fits-all, infinitely extensible, regex rewriting engine. Instead, the proxy.header directive aims to provide built-in functionality in mod_proxy for a few common use cases by performing simple host matching or urlpath prefix matching, and using the mapping of the first match. More complex use cases could possibly be handled by a custom lighttpd module (which does not currently exist). Note: the contents of the HTTP request-line and HTTP headers may or may not be in normalized canonical forms, which may or may not influence the simple matching performed. Admins should take care to provide safe defaults (fail closed) if mapping is expected to occur and blindly passing non-mapped requests is undesirable. proxy.header = ( #"map-host-request" => ( #"-" => "...",#replace provided given Host request authority #"..." => "-",#preserve existing authority (no further matching) #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-host-response" => ( #"-" => "...",#replace authority used in backend request #"..." => "-",#replace with original authority #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-urlpath" => ( #"/xxx" => "/yyy",#map one urlpath prefix to another #"/xxx/" => "/", #map one urlpath prefix to another #"/xxx" => "", #map one urlpath prefix to another #"/key" => "/value", # Note: request headers have matching "key" prefix replaced with # "value", and response headers have matching "value" prefix # replaced with "key", with a pre-test of the "value" from the # first-matched "key" in request headers (if there was a match) #), #"https-remap" => "enable", # For https requests from client, map https:// to http:// # when map-host-request matches URI in request, and map http:// # to https:// when map-host-response matches URI in response. # (mod_proxy currently sends all backend requests as http) ) x-ref: "feature to remove part of the URI when passing along requests..." https://redmine.lighttpd.net/issues/152
2017-04-28 22:53:08 +00:00
{
const array *urlpaths = remap_hdrs->urlpaths;
if (urlpaths) {
const char * const s = b->ptr+off;
const size_t plen = buffer_string_length(b) - off; /*(urlpath len)*/
if (is_req) { /* request */
for (size_t i = 0, used = urlpaths->used; i < used; ++i) {
const data_string * const ds = (data_string *)urlpaths->data[i];
const size_t mlen = buffer_string_length(&ds->key);
if (mlen <= plen && 0 == memcmp(s, ds->key.ptr, mlen)) {
[mod_proxy] simple host/url mapping in headers (fixes #152) Provide a simple mechanism for mapping host and urlpath header strings in proxied request and response well-known headers. This *is not* intended as a one-size-fits-all, infinitely extensible, regex rewriting engine. Instead, the proxy.header directive aims to provide built-in functionality in mod_proxy for a few common use cases by performing simple host matching or urlpath prefix matching, and using the mapping of the first match. More complex use cases could possibly be handled by a custom lighttpd module (which does not currently exist). Note: the contents of the HTTP request-line and HTTP headers may or may not be in normalized canonical forms, which may or may not influence the simple matching performed. Admins should take care to provide safe defaults (fail closed) if mapping is expected to occur and blindly passing non-mapped requests is undesirable. proxy.header = ( #"map-host-request" => ( #"-" => "...",#replace provided given Host request authority #"..." => "-",#preserve existing authority (no further matching) #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-host-response" => ( #"-" => "...",#replace authority used in backend request #"..." => "-",#replace with original authority #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-urlpath" => ( #"/xxx" => "/yyy",#map one urlpath prefix to another #"/xxx/" => "/", #map one urlpath prefix to another #"/xxx" => "", #map one urlpath prefix to another #"/key" => "/value", # Note: request headers have matching "key" prefix replaced with # "value", and response headers have matching "value" prefix # replaced with "key", with a pre-test of the "value" from the # first-matched "key" in request headers (if there was a match) #), #"https-remap" => "enable", # For https requests from client, map https:// to http:// # when map-host-request matches URI in request, and map http:// # to https:// when map-host-response matches URI in response. # (mod_proxy currently sends all backend requests as http) ) x-ref: "feature to remove part of the URI when passing along requests..." https://redmine.lighttpd.net/issues/152
2017-04-28 22:53:08 +00:00
if (NULL == remap_hdrs->forwarded_urlpath)
remap_hdrs->forwarded_urlpath = ds;
buffer_substr_replace(b, off, mlen, &ds->value);
return buffer_string_length(&ds->value);/*(replacement len)*/
[mod_proxy] simple host/url mapping in headers (fixes #152) Provide a simple mechanism for mapping host and urlpath header strings in proxied request and response well-known headers. This *is not* intended as a one-size-fits-all, infinitely extensible, regex rewriting engine. Instead, the proxy.header directive aims to provide built-in functionality in mod_proxy for a few common use cases by performing simple host matching or urlpath prefix matching, and using the mapping of the first match. More complex use cases could possibly be handled by a custom lighttpd module (which does not currently exist). Note: the contents of the HTTP request-line and HTTP headers may or may not be in normalized canonical forms, which may or may not influence the simple matching performed. Admins should take care to provide safe defaults (fail closed) if mapping is expected to occur and blindly passing non-mapped requests is undesirable. proxy.header = ( #"map-host-request" => ( #"-" => "...",#replace provided given Host request authority #"..." => "-",#preserve existing authority (no further matching) #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-host-response" => ( #"-" => "...",#replace authority used in backend request #"..." => "-",#replace with original authority #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-urlpath" => ( #"/xxx" => "/yyy",#map one urlpath prefix to another #"/xxx/" => "/", #map one urlpath prefix to another #"/xxx" => "", #map one urlpath prefix to another #"/key" => "/value", # Note: request headers have matching "key" prefix replaced with # "value", and response headers have matching "value" prefix # replaced with "key", with a pre-test of the "value" from the # first-matched "key" in request headers (if there was a match) #), #"https-remap" => "enable", # For https requests from client, map https:// to http:// # when map-host-request matches URI in request, and map http:// # to https:// when map-host-response matches URI in response. # (mod_proxy currently sends all backend requests as http) ) x-ref: "feature to remove part of the URI when passing along requests..." https://redmine.lighttpd.net/issues/152
2017-04-28 22:53:08 +00:00
}
}
}
else { /* response; perform reverse map */
if (NULL != remap_hdrs->forwarded_urlpath) {
const data_string * const ds = remap_hdrs->forwarded_urlpath;
const size_t mlen = buffer_string_length(&ds->value);
if (mlen <= plen && 0 == memcmp(s, ds->value.ptr, mlen)) {
buffer_substr_replace(b, off, mlen, &ds->key);
return buffer_string_length(&ds->key); /*(replacement len)*/
[mod_proxy] simple host/url mapping in headers (fixes #152) Provide a simple mechanism for mapping host and urlpath header strings in proxied request and response well-known headers. This *is not* intended as a one-size-fits-all, infinitely extensible, regex rewriting engine. Instead, the proxy.header directive aims to provide built-in functionality in mod_proxy for a few common use cases by performing simple host matching or urlpath prefix matching, and using the mapping of the first match. More complex use cases could possibly be handled by a custom lighttpd module (which does not currently exist). Note: the contents of the HTTP request-line and HTTP headers may or may not be in normalized canonical forms, which may or may not influence the simple matching performed. Admins should take care to provide safe defaults (fail closed) if mapping is expected to occur and blindly passing non-mapped requests is undesirable. proxy.header = ( #"map-host-request" => ( #"-" => "...",#replace provided given Host request authority #"..." => "-",#preserve existing authority (no further matching) #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-host-response" => ( #"-" => "...",#replace authority used in backend request #"..." => "-",#replace with original authority #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-urlpath" => ( #"/xxx" => "/yyy",#map one urlpath prefix to another #"/xxx/" => "/", #map one urlpath prefix to another #"/xxx" => "", #map one urlpath prefix to another #"/key" => "/value", # Note: request headers have matching "key" prefix replaced with # "value", and response headers have matching "value" prefix # replaced with "key", with a pre-test of the "value" from the # first-matched "key" in request headers (if there was a match) #), #"https-remap" => "enable", # For https requests from client, map https:// to http:// # when map-host-request matches URI in request, and map http:// # to https:// when map-host-response matches URI in response. # (mod_proxy currently sends all backend requests as http) ) x-ref: "feature to remove part of the URI when passing along requests..." https://redmine.lighttpd.net/issues/152
2017-04-28 22:53:08 +00:00
}
}
for (size_t i = 0, used = urlpaths->used; i < used; ++i) {
const data_string * const ds = (data_string *)urlpaths->data[i];
const size_t mlen = buffer_string_length(&ds->value);
if (mlen <= plen && 0 == memcmp(s, ds->value.ptr, mlen)) {
buffer_substr_replace(b, off, mlen, &ds->key);
return buffer_string_length(&ds->key); /*(replacement len)*/
[mod_proxy] simple host/url mapping in headers (fixes #152) Provide a simple mechanism for mapping host and urlpath header strings in proxied request and response well-known headers. This *is not* intended as a one-size-fits-all, infinitely extensible, regex rewriting engine. Instead, the proxy.header directive aims to provide built-in functionality in mod_proxy for a few common use cases by performing simple host matching or urlpath prefix matching, and using the mapping of the first match. More complex use cases could possibly be handled by a custom lighttpd module (which does not currently exist). Note: the contents of the HTTP request-line and HTTP headers may or may not be in normalized canonical forms, which may or may not influence the simple matching performed. Admins should take care to provide safe defaults (fail closed) if mapping is expected to occur and blindly passing non-mapped requests is undesirable. proxy.header = ( #"map-host-request" => ( #"-" => "...",#replace provided given Host request authority #"..." => "-",#preserve existing authority (no further matching) #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-host-response" => ( #"-" => "...",#replace authority used in backend request #"..." => "-",#replace with original authority #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-urlpath" => ( #"/xxx" => "/yyy",#map one urlpath prefix to another #"/xxx/" => "/", #map one urlpath prefix to another #"/xxx" => "", #map one urlpath prefix to another #"/key" => "/value", # Note: request headers have matching "key" prefix replaced with # "value", and response headers have matching "value" prefix # replaced with "key", with a pre-test of the "value" from the # first-matched "key" in request headers (if there was a match) #), #"https-remap" => "enable", # For https requests from client, map https:// to http:// # when map-host-request matches URI in request, and map http:// # to https:// when map-host-response matches URI in response. # (mod_proxy currently sends all backend requests as http) ) x-ref: "feature to remove part of the URI when passing along requests..." https://redmine.lighttpd.net/issues/152
2017-04-28 22:53:08 +00:00
}
}
}
}
return 0;
[mod_proxy] simple host/url mapping in headers (fixes #152) Provide a simple mechanism for mapping host and urlpath header strings in proxied request and response well-known headers. This *is not* intended as a one-size-fits-all, infinitely extensible, regex rewriting engine. Instead, the proxy.header directive aims to provide built-in functionality in mod_proxy for a few common use cases by performing simple host matching or urlpath prefix matching, and using the mapping of the first match. More complex use cases could possibly be handled by a custom lighttpd module (which does not currently exist). Note: the contents of the HTTP request-line and HTTP headers may or may not be in normalized canonical forms, which may or may not influence the simple matching performed. Admins should take care to provide safe defaults (fail closed) if mapping is expected to occur and blindly passing non-mapped requests is undesirable. proxy.header = ( #"map-host-request" => ( #"-" => "...",#replace provided given Host request authority #"..." => "-",#preserve existing authority (no further matching) #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-host-response" => ( #"-" => "...",#replace authority used in backend request #"..." => "-",#replace with original authority #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-urlpath" => ( #"/xxx" => "/yyy",#map one urlpath prefix to another #"/xxx/" => "/", #map one urlpath prefix to another #"/xxx" => "", #map one urlpath prefix to another #"/key" => "/value", # Note: request headers have matching "key" prefix replaced with # "value", and response headers have matching "value" prefix # replaced with "key", with a pre-test of the "value" from the # first-matched "key" in request headers (if there was a match) #), #"https-remap" => "enable", # For https requests from client, map https:// to http:// # when map-host-request matches URI in request, and map http:// # to https:// when map-host-response matches URI in response. # (mod_proxy currently sends all backend requests as http) ) x-ref: "feature to remove part of the URI when passing along requests..." https://redmine.lighttpd.net/issues/152
2017-04-28 22:53:08 +00:00
}
/* (future: might move to http-header-glue.c) */
static void http_header_remap_uri (buffer *b, size_t off, http_header_remap_opts *remap_hdrs, int is_req)
{
/* find beginning of URL-path (might be preceded by scheme://authority
* (caller should make sure any leading whitespace is prior to offset) */
if (b->ptr[off] != '/') {
char *s = b->ptr+off;
size_t alen; /*(authority len (host len))*/
size_t slen; /*(scheme len)*/
const buffer *m;
/* skip over scheme and authority of URI to find beginning of URL-path
* (value might conceivably be relative URL-path instead of URI) */
if (NULL == (s = strchr(s, ':')) || s[1] != '/' || s[2] != '/') return;
slen = s - (b->ptr+off);
s += 3;
off = (size_t)(s - b->ptr);
if (NULL != (s = strchr(s, '/'))) {
alen = (size_t)(s - b->ptr) - off;
if (0 == alen) return; /*(empty authority, e.g. "http:///")*/
}
else {
alen = buffer_string_length(b) - off;
if (0 == alen) return; /*(empty authority, e.g. "http:///")*/
buffer_append_string_len(b, CONST_STR_LEN("/"));
}
/* remap authority (if configured) and set offset to url-path */
m = http_header_remap_host_match(b, off, remap_hdrs, is_req, alen);
if (NULL != m) {
if (remap_hdrs->https_remap
&& (is_req ? 5==slen && 0==memcmp(b->ptr+off-slen-3,"https",5)
: 4==slen && 0==memcmp(b->ptr+off-slen-3,"http",4))){
if (is_req) {
memcpy(b->ptr+off-slen-3+4,"://",3); /*("https"=>"http")*/
--off;
++alen;
}
else {/*(!is_req)*/
memcpy(b->ptr+off-slen-3+4,"s://",4); /*("http" =>"https")*/
++off;
--alen;
}
}
buffer_substr_replace(b, off, alen, m);
alen = buffer_string_length(m);/*(length of replacement authority)*/
}
off += alen;
}
/* remap URLs (if configured) */
http_header_remap_urlpath(b, off, remap_hdrs, is_req);
}
/* (future: might move to http-header-glue.c) */
static void http_header_remap_setcookie (buffer *b, size_t off, http_header_remap_opts *remap_hdrs)
{
/* Given the special-case of Set-Cookie and the (too) loosely restricted
* characters allowed, for best results, the Set-Cookie value should be the
* entire string in b from offset to end of string. In response headers,
* lighttpd may concatenate multiple Set-Cookie headers into single entry
* in r->resp_headers, separated by "\r\nSet-Cookie: " */
for (char *s = b->ptr+off, *e; *s; s = e) {
[mod_proxy] simple host/url mapping in headers (fixes #152) Provide a simple mechanism for mapping host and urlpath header strings in proxied request and response well-known headers. This *is not* intended as a one-size-fits-all, infinitely extensible, regex rewriting engine. Instead, the proxy.header directive aims to provide built-in functionality in mod_proxy for a few common use cases by performing simple host matching or urlpath prefix matching, and using the mapping of the first match. More complex use cases could possibly be handled by a custom lighttpd module (which does not currently exist). Note: the contents of the HTTP request-line and HTTP headers may or may not be in normalized canonical forms, which may or may not influence the simple matching performed. Admins should take care to provide safe defaults (fail closed) if mapping is expected to occur and blindly passing non-mapped requests is undesirable. proxy.header = ( #"map-host-request" => ( #"-" => "...",#replace provided given Host request authority #"..." => "-",#preserve existing authority (no further matching) #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-host-response" => ( #"-" => "...",#replace authority used in backend request #"..." => "-",#replace with original authority #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-urlpath" => ( #"/xxx" => "/yyy",#map one urlpath prefix to another #"/xxx/" => "/", #map one urlpath prefix to another #"/xxx" => "", #map one urlpath prefix to another #"/key" => "/value", # Note: request headers have matching "key" prefix replaced with # "value", and response headers have matching "value" prefix # replaced with "key", with a pre-test of the "value" from the # first-matched "key" in request headers (if there was a match) #), #"https-remap" => "enable", # For https requests from client, map https:// to http:// # when map-host-request matches URI in request, and map http:// # to https:// when map-host-response matches URI in response. # (mod_proxy currently sends all backend requests as http) ) x-ref: "feature to remove part of the URI when passing along requests..." https://redmine.lighttpd.net/issues/152
2017-04-28 22:53:08 +00:00
size_t len;
{
while (*s != ';' && *s != '\n' && *s != '\0') ++s;
if (*s == '\n') {
/*(include +1 for '\n', but leave ' ' for ++s below)*/
s += sizeof("Set-Cookie:");
}
if ('\0' == *s) return;
[mod_proxy] simple host/url mapping in headers (fixes #152) Provide a simple mechanism for mapping host and urlpath header strings in proxied request and response well-known headers. This *is not* intended as a one-size-fits-all, infinitely extensible, regex rewriting engine. Instead, the proxy.header directive aims to provide built-in functionality in mod_proxy for a few common use cases by performing simple host matching or urlpath prefix matching, and using the mapping of the first match. More complex use cases could possibly be handled by a custom lighttpd module (which does not currently exist). Note: the contents of the HTTP request-line and HTTP headers may or may not be in normalized canonical forms, which may or may not influence the simple matching performed. Admins should take care to provide safe defaults (fail closed) if mapping is expected to occur and blindly passing non-mapped requests is undesirable. proxy.header = ( #"map-host-request" => ( #"-" => "...",#replace provided given Host request authority #"..." => "-",#preserve existing authority (no further matching) #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-host-response" => ( #"-" => "...",#replace authority used in backend request #"..." => "-",#replace with original authority #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-urlpath" => ( #"/xxx" => "/yyy",#map one urlpath prefix to another #"/xxx/" => "/", #map one urlpath prefix to another #"/xxx" => "", #map one urlpath prefix to another #"/key" => "/value", # Note: request headers have matching "key" prefix replaced with # "value", and response headers have matching "value" prefix # replaced with "key", with a pre-test of the "value" from the # first-matched "key" in request headers (if there was a match) #), #"https-remap" => "enable", # For https requests from client, map https:// to http:// # when map-host-request matches URI in request, and map http:// # to https:// when map-host-response matches URI in response. # (mod_proxy currently sends all backend requests as http) ) x-ref: "feature to remove part of the URI when passing along requests..." https://redmine.lighttpd.net/issues/152
2017-04-28 22:53:08 +00:00
do { ++s; } while (*s == ' ' || *s == '\t');
if ('\0' == *s) return;
e = s+1;
if ('=' == *s) continue;
[mod_proxy] simple host/url mapping in headers (fixes #152) Provide a simple mechanism for mapping host and urlpath header strings in proxied request and response well-known headers. This *is not* intended as a one-size-fits-all, infinitely extensible, regex rewriting engine. Instead, the proxy.header directive aims to provide built-in functionality in mod_proxy for a few common use cases by performing simple host matching or urlpath prefix matching, and using the mapping of the first match. More complex use cases could possibly be handled by a custom lighttpd module (which does not currently exist). Note: the contents of the HTTP request-line and HTTP headers may or may not be in normalized canonical forms, which may or may not influence the simple matching performed. Admins should take care to provide safe defaults (fail closed) if mapping is expected to occur and blindly passing non-mapped requests is undesirable. proxy.header = ( #"map-host-request" => ( #"-" => "...",#replace provided given Host request authority #"..." => "-",#preserve existing authority (no further matching) #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-host-response" => ( #"-" => "...",#replace authority used in backend request #"..." => "-",#replace with original authority #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-urlpath" => ( #"/xxx" => "/yyy",#map one urlpath prefix to another #"/xxx/" => "/", #map one urlpath prefix to another #"/xxx" => "", #map one urlpath prefix to another #"/key" => "/value", # Note: request headers have matching "key" prefix replaced with # "value", and response headers have matching "value" prefix # replaced with "key", with a pre-test of the "value" from the # first-matched "key" in request headers (if there was a match) #), #"https-remap" => "enable", # For https requests from client, map https:// to http:// # when map-host-request matches URI in request, and map http:// # to https:// when map-host-response matches URI in response. # (mod_proxy currently sends all backend requests as http) ) x-ref: "feature to remove part of the URI when passing along requests..." https://redmine.lighttpd.net/issues/152
2017-04-28 22:53:08 +00:00
/*(interested only in Domain and Path attributes)*/
while (*e != '=' && *e != '\0') ++e;
if ('\0' == *e) return;
[mod_proxy] simple host/url mapping in headers (fixes #152) Provide a simple mechanism for mapping host and urlpath header strings in proxied request and response well-known headers. This *is not* intended as a one-size-fits-all, infinitely extensible, regex rewriting engine. Instead, the proxy.header directive aims to provide built-in functionality in mod_proxy for a few common use cases by performing simple host matching or urlpath prefix matching, and using the mapping of the first match. More complex use cases could possibly be handled by a custom lighttpd module (which does not currently exist). Note: the contents of the HTTP request-line and HTTP headers may or may not be in normalized canonical forms, which may or may not influence the simple matching performed. Admins should take care to provide safe defaults (fail closed) if mapping is expected to occur and blindly passing non-mapped requests is undesirable. proxy.header = ( #"map-host-request" => ( #"-" => "...",#replace provided given Host request authority #"..." => "-",#preserve existing authority (no further matching) #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-host-response" => ( #"-" => "...",#replace authority used in backend request #"..." => "-",#replace with original authority #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-urlpath" => ( #"/xxx" => "/yyy",#map one urlpath prefix to another #"/xxx/" => "/", #map one urlpath prefix to another #"/xxx" => "", #map one urlpath prefix to another #"/key" => "/value", # Note: request headers have matching "key" prefix replaced with # "value", and response headers have matching "value" prefix # replaced with "key", with a pre-test of the "value" from the # first-matched "key" in request headers (if there was a match) #), #"https-remap" => "enable", # For https requests from client, map https:// to http:// # when map-host-request matches URI in request, and map http:// # to https:// when map-host-response matches URI in response. # (mod_proxy currently sends all backend requests as http) ) x-ref: "feature to remove part of the URI when passing along requests..." https://redmine.lighttpd.net/issues/152
2017-04-28 22:53:08 +00:00
++e;
switch ((int)(e - s - 1)) {
case 4:
if (buffer_eq_icase_ssn(s, "path", 4)) {
[mod_proxy] simple host/url mapping in headers (fixes #152) Provide a simple mechanism for mapping host and urlpath header strings in proxied request and response well-known headers. This *is not* intended as a one-size-fits-all, infinitely extensible, regex rewriting engine. Instead, the proxy.header directive aims to provide built-in functionality in mod_proxy for a few common use cases by performing simple host matching or urlpath prefix matching, and using the mapping of the first match. More complex use cases could possibly be handled by a custom lighttpd module (which does not currently exist). Note: the contents of the HTTP request-line and HTTP headers may or may not be in normalized canonical forms, which may or may not influence the simple matching performed. Admins should take care to provide safe defaults (fail closed) if mapping is expected to occur and blindly passing non-mapped requests is undesirable. proxy.header = ( #"map-host-request" => ( #"-" => "...",#replace provided given Host request authority #"..." => "-",#preserve existing authority (no further matching) #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-host-response" => ( #"-" => "...",#replace authority used in backend request #"..." => "-",#replace with original authority #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-urlpath" => ( #"/xxx" => "/yyy",#map one urlpath prefix to another #"/xxx/" => "/", #map one urlpath prefix to another #"/xxx" => "", #map one urlpath prefix to another #"/key" => "/value", # Note: request headers have matching "key" prefix replaced with # "value", and response headers have matching "value" prefix # replaced with "key", with a pre-test of the "value" from the # first-matched "key" in request headers (if there was a match) #), #"https-remap" => "enable", # For https requests from client, map https:// to http:// # when map-host-request matches URI in request, and map http:// # to https:// when map-host-response matches URI in response. # (mod_proxy currently sends all backend requests as http) ) x-ref: "feature to remove part of the URI when passing along requests..." https://redmine.lighttpd.net/issues/152
2017-04-28 22:53:08 +00:00
if (*e == '"') ++e;
if (*e != '/') continue;
off = (size_t)(e - b->ptr);
len = http_header_remap_urlpath(b, off, remap_hdrs, 0);
e = b->ptr+off+len; /*(b may have been reallocated)*/
[mod_proxy] simple host/url mapping in headers (fixes #152) Provide a simple mechanism for mapping host and urlpath header strings in proxied request and response well-known headers. This *is not* intended as a one-size-fits-all, infinitely extensible, regex rewriting engine. Instead, the proxy.header directive aims to provide built-in functionality in mod_proxy for a few common use cases by performing simple host matching or urlpath prefix matching, and using the mapping of the first match. More complex use cases could possibly be handled by a custom lighttpd module (which does not currently exist). Note: the contents of the HTTP request-line and HTTP headers may or may not be in normalized canonical forms, which may or may not influence the simple matching performed. Admins should take care to provide safe defaults (fail closed) if mapping is expected to occur and blindly passing non-mapped requests is undesirable. proxy.header = ( #"map-host-request" => ( #"-" => "...",#replace provided given Host request authority #"..." => "-",#preserve existing authority (no further matching) #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-host-response" => ( #"-" => "...",#replace authority used in backend request #"..." => "-",#replace with original authority #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-urlpath" => ( #"/xxx" => "/yyy",#map one urlpath prefix to another #"/xxx/" => "/", #map one urlpath prefix to another #"/xxx" => "", #map one urlpath prefix to another #"/key" => "/value", # Note: request headers have matching "key" prefix replaced with # "value", and response headers have matching "value" prefix # replaced with "key", with a pre-test of the "value" from the # first-matched "key" in request headers (if there was a match) #), #"https-remap" => "enable", # For https requests from client, map https:// to http:// # when map-host-request matches URI in request, and map http:// # to https:// when map-host-response matches URI in response. # (mod_proxy currently sends all backend requests as http) ) x-ref: "feature to remove part of the URI when passing along requests..." https://redmine.lighttpd.net/issues/152
2017-04-28 22:53:08 +00:00
continue;
}
break;
case 6:
if (buffer_eq_icase_ssn(s, "domain", 6)) {
[mod_proxy] simple host/url mapping in headers (fixes #152) Provide a simple mechanism for mapping host and urlpath header strings in proxied request and response well-known headers. This *is not* intended as a one-size-fits-all, infinitely extensible, regex rewriting engine. Instead, the proxy.header directive aims to provide built-in functionality in mod_proxy for a few common use cases by performing simple host matching or urlpath prefix matching, and using the mapping of the first match. More complex use cases could possibly be handled by a custom lighttpd module (which does not currently exist). Note: the contents of the HTTP request-line and HTTP headers may or may not be in normalized canonical forms, which may or may not influence the simple matching performed. Admins should take care to provide safe defaults (fail closed) if mapping is expected to occur and blindly passing non-mapped requests is undesirable. proxy.header = ( #"map-host-request" => ( #"-" => "...",#replace provided given Host request authority #"..." => "-",#preserve existing authority (no further matching) #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-host-response" => ( #"-" => "...",#replace authority used in backend request #"..." => "-",#replace with original authority #"..." => "", #preserve existing authority (no further matching) # #(equivalent to "xxx" => "xxx") #"xxx" => "yyy", #map one string ("xxx") to another ("yyy") #), #"map-urlpath" => ( #"/xxx" => "/yyy",#map one urlpath prefix to another #"/xxx/" => "/", #map one urlpath prefix to another #"/xxx" => "", #map one urlpath prefix to another #"/key" => "/value", # Note: request headers have matching "key" prefix replaced with # "value", and response headers have matching "value" prefix # replaced with "key", with a pre-test of the "value" from the # first-matched "key" in request headers (if there was a match) #), #"https-remap" => "enable", # For https requests from client, map https:// to http:// # when map-host-request matches URI in request, and map http:// # to https:// when map-host-response matches URI in response. # (mod_proxy currently sends all backend requests as http) ) x-ref: "feature to remove part of the URI when passing along requests..." https://redmine.lighttpd.net/issues/152
2017-04-28 22:53:08 +00:00
size_t alen = 0;
if (*e == '"') ++e;
if (*e == '.') ++e;
if (*e == ';') continue;
off = (size_t)(e - b->ptr);
for (char c; (c = e[alen]) != ';' && c != ' ' && c != '\t'
&& c != '\r' && c != '\0'; ++alen);
len = http_header_remap_host(b, off, remap_hdrs, 0, alen);
e = b->ptr+off+len; /*(b may have been reallocated)*/
continue;
}
break;
default:
break;
}
}
}
}
static void buffer_append_string_backslash_escaped(buffer *b, const char *s, size_t len) {
/* (future: might move to buffer.c) */
size_t j = 0;
char *p;
buffer_string_prepare_append(b, len*2 + 4);
p = b->ptr + buffer_string_length(b);
for (size_t i = 0; i < len; ++i) {
int c = s[i];
if (c == '"' || c == '\\' || c == 0x7F || (c < 0x20 && c != '\t'))
p[j++] = '\\';
p[j++] = c;
}
buffer_commit(b, j);
}
static void proxy_set_Forwarded(connection * const con, request_st * const r, const unsigned int flags) {
buffer *b = NULL, *efor = NULL, *eproto = NULL, *ehost = NULL;
int semicolon = 0;
if (proxy_check_extforward) {
efor =
http_header_env_get(r, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_FOR"));
eproto =
http_header_env_get(r, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_PROTO"));
ehost =
http_header_env_get(r, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_HOST"));
}
/* note: set "Forwarded" prior to updating X-Forwarded-For (below) */
if (flags)
b = http_header_request_get(r, HTTP_HEADER_FORWARDED, CONST_STR_LEN("Forwarded"));
if (flags && NULL == b) {
const buffer *xff =
http_header_request_get(r, HTTP_HEADER_X_FORWARDED_FOR, CONST_STR_LEN("X-Forwarded-For"));
http_header_request_set(r, HTTP_HEADER_FORWARDED,
CONST_STR_LEN("Forwarded"),
CONST_STR_LEN("x")); /*(must not be blank for _get below)*/
#ifdef __COVERITY__
force_assert(NULL != b); /*(not NULL because created directly above)*/
#endif
b = http_header_request_get(r, HTTP_HEADER_FORWARDED, CONST_STR_LEN("Forwarded"));
buffer_clear(b);
if (NULL != xff) {
/* use X-Forwarded-For contents to seed Forwarded */
char *s = xff->ptr;
size_t used = buffer_string_length(xff);
for (size_t i=0, j, ipv6; i < used; ++i) {
while (s[i] == ' ' || s[i] == '\t' || s[i] == ',') ++i;
if (s[i] == '\0') break;
j = i;
do {
++i;
} while (s[i]!=' ' && s[i]!='\t' && s[i]!=',' && s[i]!='\0');
buffer_append_string_len(b, CONST_STR_LEN("for="));
/* over-simplified test expecting only IPv4 or IPv6 addresses,
* (not expecting :port, so treat existence of colon as IPv6,
* and not expecting unix paths, especially not containing ':')
* quote all strings, backslash-escape since IPs not validated*/
ipv6 = (NULL != memchr(s+j, ':', i-j)); /*(over-simplified) */
buffer_append_string_len(b, CONST_STR_LEN("\""));
if (ipv6)
buffer_append_string_len(b, CONST_STR_LEN("["));
buffer_append_string_backslash_escaped(b, s+j, i-j);
if (ipv6)
buffer_append_string_len(b, CONST_STR_LEN("]"));
buffer_append_string_len(b, CONST_STR_LEN("\""));
buffer_append_string_len(b, CONST_STR_LEN(", "));