1 #include "first.h" 2 3 #include <sys/types.h> 4 #include <errno.h> 5 #include <limits.h> 6 #include <stdlib.h> 7 #include <string.h> 8 9 #include "gw_backend.h" 10 typedef gw_plugin_config plugin_config; 11 typedef gw_plugin_data plugin_data; 12 typedef gw_handler_ctx handler_ctx; 13 14 #include "base.h" 15 #include "buffer.h" 16 #include "log.h" 17 #include "status_counter.h" 18 19 #include "sys-endian.h" 20 21 enum { LI_PROTOCOL_SCGI, LI_PROTOCOL_UWSGI }; 22 23 SETDEFAULTS_FUNC(mod_scgi_set_defaults) { 24 plugin_data *p = p_d; 25 data_unset *du; 26 size_t i = 0; 27 28 config_values_t cv[] = { 29 { "scgi.server", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ 30 { "scgi.debug", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ 31 { "scgi.protocol", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ 32 { "scgi.map-extensions", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ 33 { "scgi.balance", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ 34 { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } 35 }; 36 37 p->config_storage = calloc(srv->config_context->used, sizeof(plugin_config *)); 38 force_assert(p->config_storage); 39 40 for (i = 0; i < srv->config_context->used; i++) { 41 data_config const* config = (data_config const*)srv->config_context->data[i]; 42 plugin_config *s; 43 44 s = calloc(1, sizeof(plugin_config)); 45 force_assert(s); 46 s->exts = NULL; 47 s->exts_auth = NULL; 48 s->exts_resp = NULL; 49 s->debug = 0; 50 s->proto = LI_PROTOCOL_SCGI; 51 s->ext_mapping = array_init(); 52 53 cv[0].destination = s->exts; /* not used; T_CONFIG_LOCAL */ 54 cv[1].destination = &(s->debug); 55 cv[2].destination = NULL; /* not used; T_CONFIG_LOCAL */ 56 cv[3].destination = s->ext_mapping; 57 cv[4].destination = NULL; /* not used; T_CONFIG_LOCAL */ 58 59 p->config_storage[i] = s; 60 61 if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { 62 return HANDLER_ERROR; 63 } 64 65 du = array_get_element(config->value, "scgi.server"); 66 if (!gw_set_defaults_backend(srv, p, du, i, 1)) { 67 return HANDLER_ERROR; 68 } 69 70 du = array_get_element(config->value, "scgi.balance"); 71 if (!gw_set_defaults_balance(srv, s, du)) { 72 return HANDLER_ERROR; 73 } 74 75 if (NULL != (du = array_get_element(config->value, "scgi.protocol"))) { 76 data_string *ds = (data_string *)du; 77 if (du->type == TYPE_STRING 78 && buffer_is_equal_string(ds->value, CONST_STR_LEN("scgi"))) { 79 s->proto = LI_PROTOCOL_SCGI; 80 } else if (du->type == TYPE_STRING 81 && buffer_is_equal_string(ds->value, CONST_STR_LEN("uwsgi"))) { 82 s->proto = LI_PROTOCOL_UWSGI; 83 } else { 84 log_error_write(srv, __FILE__, __LINE__, "sss", 85 "unexpected type for key: ", "scgi.protocol", "expected \"scgi\" or \"uwsgi\""); 86 87 return HANDLER_ERROR; 88 } 89 } 90 } 91 92 return HANDLER_GO_ON; 93 } 94 95 static int scgi_env_add_scgi(void *venv, const char *key, size_t key_len, const char *val, size_t val_len) { 96 buffer *env = venv; 97 char *dst; 98 size_t len; 99 100 if (!key || !val) return -1; 101 102 len = key_len + val_len + 2; 103 104 if (buffer_string_space(env) < len) { 105 size_t extend = env->size * 2 - buffer_string_length(env); 106 extend = extend > len ? extend : len + 4095; 107 buffer_string_prepare_append(env, extend); 108 } 109 110 dst = buffer_string_prepare_append(env, len); 111 memcpy(dst, key, key_len); 112 dst[key_len] = '\0'; 113 memcpy(dst + key_len + 1, val, val_len); 114 dst[key_len + 1 + val_len] = '\0'; 115 buffer_commit(env, len); 116 117 return 0; 118 } 119 120 121 #ifdef __LITTLE_ENDIAN__ 122 #define uwsgi_htole16(x) (x) 123 #else /* __BIG_ENDIAN__ */ 124 #define uwsgi_htole16(x) ((uint16_t) (((x) & 0xff) << 8 | ((x) & 0xff00) >> 8)) 125 #endif 126 127 128 static int scgi_env_add_uwsgi(void *venv, const char *key, size_t key_len, const char *val, size_t val_len) { 129 buffer *env = venv; 130 char *dst; 131 size_t len; 132 uint16_t uwlen; 133 134 if (!key || !val) return -1; 135 if (key_len > USHRT_MAX || val_len > USHRT_MAX) return -1; 136 137 len = 2 + key_len + 2 + val_len; 138 139 if (buffer_string_space(env) < len) { 140 size_t extend = env->size * 2 - buffer_string_length(env); 141 extend = extend > len ? extend : len + 4095; 142 buffer_string_prepare_append(env, extend); 143 } 144 145 dst = buffer_string_prepare_append(env, len); 146 uwlen = uwsgi_htole16((uint16_t)key_len); 147 memcpy(dst, (char *)&uwlen, 2); 148 memcpy(dst + 2, key, key_len); 149 uwlen = uwsgi_htole16((uint16_t)val_len); 150 memcpy(dst + 2 + key_len, (char *)&uwlen, 2); 151 memcpy(dst + 2 + key_len + 2, val, val_len); 152 buffer_commit(env, len); 153 154 return 0; 155 } 156 157 158 static handler_t scgi_create_env(server *srv, handler_ctx *hctx) { 159 gw_host *host = hctx->host; 160 connection *con = hctx->remote_conn; 161 http_cgi_opts opts = { 0, 0, host->docroot, NULL }; 162 http_cgi_header_append_cb scgi_env_add = hctx->conf.proto == LI_PROTOCOL_SCGI 163 ? scgi_env_add_scgi 164 : scgi_env_add_uwsgi; 165 size_t offset; 166 size_t rsz = (size_t)(con->read_queue->bytes_out - hctx->wb->bytes_in); 167 buffer * const b = chunkqueue_prepend_buffer_open_sz(hctx->wb, rsz < 65536 ? rsz : con->header_len); 168 169 /* save space for 9 digits (plus ':'), though incoming HTTP request 170 * currently limited to 64k (65535, so 5 chars) */ 171 buffer_copy_string_len(b, CONST_STR_LEN(" ")); 172 173 if (0 != http_cgi_headers(srv, con, &opts, scgi_env_add, b)) { 174 con->http_status = 400; 175 con->mode = DIRECT; 176 buffer_clear(b); 177 chunkqueue_remove_finished_chunks(hctx->wb); 178 return HANDLER_FINISHED; 179 } 180 181 if (hctx->conf.proto == LI_PROTOCOL_SCGI) { 182 size_t len; 183 scgi_env_add(b, CONST_STR_LEN("SCGI"), CONST_STR_LEN("1")); 184 buffer_clear(srv->tmp_buf); 185 buffer_append_int(srv->tmp_buf, buffer_string_length(b)-10); 186 buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN(":")); 187 len = buffer_string_length(srv->tmp_buf); 188 offset = 10 - len; 189 memcpy(b->ptr+offset, srv->tmp_buf->ptr, len); 190 buffer_append_string_len(b, CONST_STR_LEN(",")); 191 } else { /* LI_PROTOCOL_UWSGI */ 192 /* http://uwsgi-docs.readthedocs.io/en/latest/Protocol.html */ 193 size_t len = buffer_string_length(b)-10; 194 uint32_t uwsgi_header; 195 if (len > USHRT_MAX) { 196 con->http_status = 431; /* Request Header Fields Too Large */ 197 con->mode = DIRECT; 198 buffer_clear(b); 199 chunkqueue_remove_finished_chunks(hctx->wb); 200 return HANDLER_FINISHED; 201 } 202 offset = 10 - 4; 203 uwsgi_header = ((uint32_t)uwsgi_htole16((uint16_t)len)) << 8; 204 memcpy(b->ptr+offset, (char *)&uwsgi_header, 4); 205 } 206 207 hctx->wb_reqlen = buffer_string_length(b) - offset; 208 chunkqueue_prepend_buffer_commit(hctx->wb); 209 #if 0 210 hctx->wb->first->offset += (off_t)offset; 211 hctx->wb->bytes_in -= (off_t)offset; 212 #else 213 chunkqueue_mark_written(hctx->wb, offset); 214 #endif 215 216 if (con->request.content_length) { 217 chunkqueue_append_chunkqueue(hctx->wb, con->request_content_queue); 218 if (con->request.content_length > 0) 219 hctx->wb_reqlen += con->request.content_length; /* total req size */ 220 else /* as-yet-unknown total request size (Transfer-Encoding: chunked)*/ 221 hctx->wb_reqlen = -hctx->wb_reqlen; 222 } 223 224 status_counter_inc(srv, CONST_STR_LEN("scgi.requests")); 225 return HANDLER_GO_ON; 226 } 227 228 #define PATCH(x) \ 229 p->conf.x = s->x; 230 static int scgi_patch_connection(server *srv, connection *con, plugin_data *p) { 231 size_t i, j; 232 plugin_config *s = p->config_storage[0]; 233 234 PATCH(exts); 235 PATCH(exts_auth); 236 PATCH(exts_resp); 237 PATCH(proto); 238 PATCH(debug); 239 PATCH(balance); 240 PATCH(ext_mapping); 241 242 /* skip the first, the global context */ 243 for (i = 1; i < srv->config_context->used; i++) { 244 data_config *dc = (data_config *)srv->config_context->data[i]; 245 s = p->config_storage[i]; 246 247 /* condition didn't match */ 248 if (!config_check_cond(srv, con, dc)) continue; 249 250 /* merge config */ 251 for (j = 0; j < dc->value->used; j++) { 252 data_unset *du = dc->value->data[j]; 253 254 if (buffer_is_equal_string(du->key, CONST_STR_LEN("scgi.server"))) { 255 PATCH(exts); 256 PATCH(exts_auth); 257 PATCH(exts_resp); 258 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("scgi.protocol"))) { 259 PATCH(proto); 260 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("scgi.balance"))) { 261 PATCH(balance); 262 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("scgi.debug"))) { 263 PATCH(debug); 264 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("scgi.map-extensions"))) { 265 PATCH(ext_mapping); 266 } 267 } 268 } 269 270 return 0; 271 } 272 #undef PATCH 273 274 275 static handler_t scgi_check_extension(server *srv, connection *con, void *p_d, int uri_path_handler) { 276 plugin_data *p = p_d; 277 handler_t rc; 278 279 if (con->mode != DIRECT) return HANDLER_GO_ON; 280 281 scgi_patch_connection(srv, con, p); 282 if (NULL == p->conf.exts) return HANDLER_GO_ON; 283 284 rc = gw_check_extension(srv, con, p, uri_path_handler, 0); 285 if (HANDLER_GO_ON != rc) return rc; 286 287 if (con->mode == p->id) { 288 handler_ctx *hctx = con->plugin_ctx[p->id]; 289 hctx->opts.backend = BACKEND_SCGI; 290 hctx->create_env = scgi_create_env; 291 hctx->response = chunk_buffer_acquire(); 292 } 293 294 return HANDLER_GO_ON; 295 } 296 297 /* uri-path handler */ 298 static handler_t scgi_check_extension_1(server *srv, connection *con, void *p_d) { 299 return scgi_check_extension(srv, con, p_d, 1); 300 } 301 302 /* start request handler */ 303 static handler_t scgi_check_extension_2(server *srv, connection *con, void *p_d) { 304 return scgi_check_extension(srv, con, p_d, 0); 305 } 306 307 308 309 int mod_scgi_plugin_init(plugin *p); 310 int mod_scgi_plugin_init(plugin *p) { 311 p->version = LIGHTTPD_VERSION_ID; 312 p->name = buffer_init_string("scgi"); 313 314 p->init = gw_init; 315 p->cleanup = gw_free; 316 p->set_defaults = mod_scgi_set_defaults; 317 p->connection_reset = gw_connection_reset; 318 p->handle_uri_clean = scgi_check_extension_1; 319 p->handle_subrequest_start = scgi_check_extension_2; 320 p->handle_subrequest = gw_handle_subrequest; 321 p->handle_trigger = gw_handle_trigger; 322 p->handle_waitpid = gw_handle_waitpid_cb; 323 324 p->data = NULL; 325 326 return 0; 327 } 328