1 /*
2 * mod_sockproxy - socket-level proxy support for lighttpd
3 *
4 * Copyright(c) 2018 Glenn Strauss gstrauss()gluelogic.com All rights reserved
5 * License: BSD 3-clause (same as lighttpd)
6 */
7 #include "first.h"
8
9 #include <stdlib.h>
10 #include <string.h>
11
12 #include "gw_backend.h"
13 typedef gw_plugin_config plugin_config;
14 typedef gw_plugin_data plugin_data;
15 typedef gw_handler_ctx handler_ctx;
16
17 #include "base.h"
18 #include "array.h"
19 #include "buffer.h"
20 #include "log.h"
21
22 /**
23 *
24 * socket proxy (with optional buffering)
25 *
26 */
27
mod_sockproxy_merge_config_cpv(plugin_config * const pconf,const config_plugin_value_t * const cpv)28 static void mod_sockproxy_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) {
29 switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */
30 case 0: /* sockproxy.server */
31 if (cpv->vtype == T_CONFIG_LOCAL) {
32 gw_plugin_config * const gw = cpv->v.v;
33 pconf->exts = gw->exts;
34 pconf->exts_auth = gw->exts_auth;
35 pconf->exts_resp = gw->exts_resp;
36 }
37 break;
38 case 1: /* sockproxy.balance */
39 /*if (cpv->vtype == T_CONFIG_LOCAL)*//*always true here for this param*/
40 pconf->balance = (int)cpv->v.u;
41 break;
42 case 2: /* sockproxy.debug */
43 pconf->debug = (int)cpv->v.u;
44 break;
45 default:/* should not happen */
46 return;
47 }
48 }
49
mod_sockproxy_merge_config(plugin_config * const pconf,const config_plugin_value_t * cpv)50 static void mod_sockproxy_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) {
51 do {
52 mod_sockproxy_merge_config_cpv(pconf, cpv);
53 } while ((++cpv)->k_id != -1);
54 }
55
mod_sockproxy_patch_config(request_st * const r,plugin_data * const p)56 static void mod_sockproxy_patch_config(request_st * const r, plugin_data * const p) {
57 memcpy(&p->conf, &p->defaults, sizeof(plugin_config));
58 for (int i = 1, used = p->nconfig; i < used; ++i) {
59 if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id))
60 mod_sockproxy_merge_config(&p->conf,p->cvlist+p->cvlist[i].v.u2[0]);
61 }
62 }
63
SETDEFAULTS_FUNC(mod_sockproxy_set_defaults)64 SETDEFAULTS_FUNC(mod_sockproxy_set_defaults) {
65 static const config_plugin_keys_t cpk[] = {
66 { CONST_STR_LEN("sockproxy.server"),
67 T_CONFIG_ARRAY_KVARRAY,
68 T_CONFIG_SCOPE_CONNECTION }
69 ,{ CONST_STR_LEN("sockproxy.balance"),
70 T_CONFIG_STRING,
71 T_CONFIG_SCOPE_CONNECTION }
72 ,{ CONST_STR_LEN("sockproxy.debug"),
73 T_CONFIG_INT,
74 T_CONFIG_SCOPE_CONNECTION }
75 ,{ NULL, 0,
76 T_CONFIG_UNSET,
77 T_CONFIG_SCOPE_UNSET }
78 };
79
80 plugin_data * const p = p_d;
81 if (!config_plugin_values_init(srv, p, cpk, "mod_sockproxy"))
82 return HANDLER_ERROR;
83
84 /* process and validate config directives
85 * (init i to 0 if global context; to 1 to skip empty global context) */
86 for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
87 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
88 gw_plugin_config *gw = NULL;
89 for (; -1 != cpv->k_id; ++cpv) {
90 switch (cpv->k_id) {
91 case 0: /* sockproxy.server */
92 gw = ck_calloc(1, sizeof(gw_plugin_config));
93 if (!gw_set_defaults_backend(srv, p, cpv->v.a, gw, 0,
94 cpk[cpv->k_id].k)) {
95 gw_plugin_config_free(gw);
96 return HANDLER_ERROR;
97 }
98 cpv->v.v = gw;
99 cpv->vtype = T_CONFIG_LOCAL;
100 break;
101 case 1: /* sockproxy.balance */
102 cpv->v.u = (unsigned int)gw_get_defaults_balance(srv, cpv->v.b);
103 break;
104 case 2: /* sockproxy.debug */
105 break;
106 default:/* should not happen */
107 break;
108 }
109 }
110
111 /* disable check-local for all exts (default enabled) */
112 if (gw && gw->exts) { /*(check after gw_set_defaults_backend())*/
113 gw_exts_clear_check_local(gw->exts);
114 }
115 }
116
117 /* default is 0 */
118 /*p->defaults.balance = (unsigned int)gw_get_defaults_balance(srv, NULL);*/
119
120 /* initialize p->defaults from global config context */
121 if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
122 const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
123 if (-1 != cpv->k_id)
124 mod_sockproxy_merge_config(&p->defaults, cpv);
125 }
126
127 return HANDLER_GO_ON;
128 }
129
130
sockproxy_create_env_connect(handler_ctx * hctx)131 static handler_t sockproxy_create_env_connect(handler_ctx *hctx) {
132 request_st * const r = hctx->r;
133 r->resp_body_started = 1;
134 gw_set_transparent(hctx);
135 http_response_upgrade_read_body_unknown(r);
136
137 plugin_stats_inc("sockproxy.requests");
138 return HANDLER_GO_ON;
139 }
140
141
mod_sockproxy_connection_accept(connection * con,void * p_d)142 static handler_t mod_sockproxy_connection_accept(connection *con, void *p_d) {
143 request_st * const r = &con->request;
144 plugin_data *p = p_d;
145 handler_t rc;
146
147 if (NULL != r->handler_module) return HANDLER_GO_ON;
148
149 mod_sockproxy_patch_config(r, p);
150 if (NULL == p->conf.exts) return HANDLER_GO_ON;
151
152 /*(fake r->uri.path for matching purposes in gw_check_extension())*/
153 buffer_copy_string_len(&r->uri.path, CONST_STR_LEN("/"));
154
155 rc = gw_check_extension(r, p, 1, 0);
156 if (HANDLER_GO_ON != rc) return rc;
157
158 if (r->handler_module == p->self) {
159 handler_ctx *hctx = r->plugin_ctx[p->id];
160 hctx->opts.backend = BACKEND_PROXY;
161 hctx->create_env = sockproxy_create_env_connect;
162 hctx->response = chunk_buffer_acquire();
163 r->http_status = -1; /*(skip HTTP processing)*/
164 r->http_version = HTTP_VERSION_UNSET;
165 }
166
167 return HANDLER_GO_ON;
168 }
169
170
171 __attribute_cold__
172 int mod_sockproxy_plugin_init(plugin *p);
mod_sockproxy_plugin_init(plugin * p)173 int mod_sockproxy_plugin_init(plugin *p) {
174 p->version = LIGHTTPD_VERSION_ID;
175 p->name = "sockproxy";
176
177 p->init = gw_init;
178 p->cleanup = gw_free;
179 p->set_defaults = mod_sockproxy_set_defaults;
180 p->handle_request_reset = gw_handle_request_reset;
181 p->handle_connection_accept= mod_sockproxy_connection_accept;
182 p->handle_subrequest = gw_handle_subrequest;
183 p->handle_trigger = gw_handle_trigger;
184 p->handle_waitpid = gw_handle_waitpid_cb;
185
186 return 0;
187 }
188