1 #include "first.h" 2 3 #include <sys/types.h> 4 #include <stdlib.h> 5 #include <string.h> 6 7 #include "gw_backend.h" 8 typedef gw_plugin_config plugin_config; 9 typedef gw_plugin_data plugin_data; 10 typedef gw_handler_ctx handler_ctx; 11 12 #include "buffer.h" 13 #include "fdevent.h" 14 #include "http_cgi.h" 15 #include "http_chunk.h" 16 #include "log.h" 17 #include "request.h" 18 #include "status_counter.h" 19 20 #include "compat/fastcgi.h" 21 22 #if GW_RESPONDER != FCGI_RESPONDER 23 #error "mismatched defines: (GW_RESPONDER != FCGI_RESPONDER)" 24 #endif 25 #if GW_AUTHORIZER != FCGI_AUTHORIZER 26 #error "mismatched defines: (GW_AUTHORIZER != FCGI_AUTHORIZER)" 27 #endif 28 #if GW_FILTER != FCGI_FILTER 29 #error "mismatched defines: (GW_FILTER != FCGI_FILTER)" 30 #endif 31 32 static void mod_fastcgi_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) { 33 switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ 34 case 0: /* fastcgi.server */ 35 if (cpv->vtype == T_CONFIG_LOCAL) { 36 gw_plugin_config * const gw = cpv->v.v; 37 pconf->exts = gw->exts; 38 pconf->exts_auth = gw->exts_auth; 39 pconf->exts_resp = gw->exts_resp; 40 } 41 break; 42 case 1: /* fastcgi.balance */ 43 /*if (cpv->vtype == T_CONFIG_LOCAL)*//*always true here for this param*/ 44 pconf->balance = (int)cpv->v.u; 45 break; 46 case 2: /* fastcgi.debug */ 47 pconf->debug = (int)cpv->v.u; 48 break; 49 case 3: /* fastcgi.map-extensions */ 50 pconf->ext_mapping = cpv->v.a; 51 break; 52 default:/* should not happen */ 53 return; 54 } 55 } 56 57 static void mod_fastcgi_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) { 58 do { 59 mod_fastcgi_merge_config_cpv(pconf, cpv); 60 } while ((++cpv)->k_id != -1); 61 } 62 63 static void mod_fastcgi_patch_config(request_st * const r, plugin_data * const p) { 64 memcpy(&p->conf, &p->defaults, sizeof(plugin_config)); 65 for (int i = 1, used = p->nconfig; i < used; ++i) { 66 if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id)) 67 mod_fastcgi_merge_config(&p->conf,p->cvlist + p->cvlist[i].v.u2[0]); 68 } 69 } 70 71 SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) { 72 static const config_plugin_keys_t cpk[] = { 73 { CONST_STR_LEN("fastcgi.server"), 74 T_CONFIG_ARRAY_KVARRAY, 75 T_CONFIG_SCOPE_CONNECTION } 76 ,{ CONST_STR_LEN("fastcgi.balance"), 77 T_CONFIG_STRING, 78 T_CONFIG_SCOPE_CONNECTION } 79 ,{ CONST_STR_LEN("fastcgi.debug"), 80 T_CONFIG_INT, 81 T_CONFIG_SCOPE_CONNECTION } 82 ,{ CONST_STR_LEN("fastcgi.map-extensions"), 83 T_CONFIG_ARRAY_KVSTRING, 84 T_CONFIG_SCOPE_CONNECTION } 85 ,{ NULL, 0, 86 T_CONFIG_UNSET, 87 T_CONFIG_SCOPE_UNSET } 88 }; 89 90 plugin_data * const p = p_d; 91 if (!config_plugin_values_init(srv, p, cpk, "mod_fastcgi")) 92 return HANDLER_ERROR; 93 94 /* process and validate config directives 95 * (init i to 0 if global context; to 1 to skip empty global context) */ 96 for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { 97 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 98 for (; -1 != cpv->k_id; ++cpv) { 99 switch (cpv->k_id) { 100 case 0:{/* fastcgi.server */ 101 gw_plugin_config *gw = calloc(1, sizeof(gw_plugin_config)); 102 force_assert(gw); 103 if (!gw_set_defaults_backend(srv, p, cpv->v.a, gw, 0, 104 cpk[cpv->k_id].k)) { 105 gw_plugin_config_free(gw); 106 return HANDLER_ERROR; 107 } 108 cpv->v.v = gw; 109 cpv->vtype = T_CONFIG_LOCAL; 110 break; 111 } 112 case 1: /* fastcgi.balance */ 113 cpv->v.u = (unsigned int)gw_get_defaults_balance(srv, cpv->v.b); 114 break; 115 case 2: /* fastcgi.debug */ 116 case 3: /* fastcgi.map-extensions */ 117 break; 118 default:/* should not happen */ 119 break; 120 } 121 } 122 } 123 124 /* default is 0 */ 125 /*p->defaults.balance = (unsigned int)gw_get_defaults_balance(srv, NULL);*/ 126 127 /* initialize p->defaults from global config context */ 128 if (p->nconfig > 0 && p->cvlist->v.u2[1]) { 129 const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; 130 if (-1 != cpv->k_id) 131 mod_fastcgi_merge_config(&p->defaults, cpv); 132 } 133 134 return HANDLER_GO_ON; 135 } 136 137 138 static int fcgi_env_add(void *venv, const char *key, size_t key_len, const char *val, size_t val_len) { 139 buffer *env = venv; 140 char len_enc[8]; 141 size_t len_enc_len = 0; 142 143 if (!key || (!val && val_len)) return -1; 144 145 /** 146 * field length can be 31bit max 147 * 148 * HINT: this can't happen as FCGI_MAX_LENGTH is only 16bit 149 */ 150 force_assert(key_len < 0x7fffffffu); 151 force_assert(val_len < 0x7fffffffu); 152 153 if (key_len > 127) { 154 len_enc[0] = ((key_len >> 24) & 0xff) | 0x80; 155 len_enc[1] = (key_len >> 16) & 0xff; 156 len_enc[2] = (key_len >> 8) & 0xff; 157 len_enc_len += 3; 158 } 159 len_enc[len_enc_len++] = key_len & 0xff; 160 161 if (val_len > 127) { 162 len_enc[len_enc_len++] = ((val_len >> 24) & 0xff) | 0x80; 163 len_enc[len_enc_len++] = (val_len >> 16) & 0xff; 164 len_enc[len_enc_len++] = (val_len >> 8) & 0xff; 165 } 166 len_enc[len_enc_len++] = val_len & 0xff; 167 168 const size_t len = len_enc_len + key_len + val_len; 169 const size_t fmax = 170 FCGI_MAX_LENGTH + sizeof(FCGI_BeginRequestRecord) + sizeof(FCGI_Header); 171 if (len > fmax - buffer_clen(env)) 172 return -1; /* we can't append more headers, ignore it */ 173 174 buffer_append_str3(env, len_enc, len_enc_len, key, key_len, val, val_len); 175 return 0; 176 } 177 178 static void fcgi_header(FCGI_Header * header, unsigned char type, int request_id, int contentLength, unsigned char paddingLength) { 179 force_assert(contentLength <= FCGI_MAX_LENGTH); 180 181 header->version = FCGI_VERSION_1; 182 header->type = type; 183 header->requestIdB0 = request_id & 0xff; 184 header->requestIdB1 = (request_id >> 8) & 0xff; 185 header->contentLengthB0 = contentLength & 0xff; 186 header->contentLengthB1 = (contentLength >> 8) & 0xff; 187 header->paddingLength = paddingLength; 188 header->reserved = 0; 189 } 190 191 static handler_t fcgi_stdin_append(handler_ctx *hctx) { 192 FCGI_Header header; 193 chunkqueue * const req_cq = &hctx->r->reqbody_queue; 194 off_t offset, weWant; 195 off_t req_cqlen = chunkqueue_length(req_cq); 196 int request_id = hctx->request_id; 197 if (req_cqlen > MAX_WRITE_LIMIT) req_cqlen = MAX_WRITE_LIMIT; 198 199 /* something to send ? */ 200 for (offset = 0; offset != req_cqlen; offset += weWant) { 201 weWant = req_cqlen - offset > FCGI_MAX_LENGTH ? FCGI_MAX_LENGTH : req_cqlen - offset; 202 203 if (-1 != hctx->wb_reqlen) { 204 if (hctx->wb_reqlen >= 0) { 205 hctx->wb_reqlen += sizeof(header); 206 } else { 207 hctx->wb_reqlen -= sizeof(header); 208 } 209 } 210 211 fcgi_header(&(header), FCGI_STDIN, request_id, weWant, 0); 212 (chunkqueue_is_empty(&hctx->wb) || hctx->wb.first->type == MEM_CHUNK) /* else FILE_CHUNK for temp file */ 213 ? chunkqueue_append_mem(&hctx->wb, (const char *)&header, sizeof(header)) 214 : chunkqueue_append_mem_min(&hctx->wb, (const char *)&header, sizeof(header)); 215 chunkqueue_steal(&hctx->wb, req_cq, weWant); 216 /*(hctx->wb_reqlen already includes reqbody_length)*/ 217 } 218 219 if (hctx->wb.bytes_in == hctx->wb_reqlen) { 220 /* terminate STDIN */ 221 /* (future: must defer ending FCGI_STDIN 222 * if might later upgrade protocols 223 * and then have more data to send) */ 224 fcgi_header(&(header), FCGI_STDIN, request_id, 0, 0); 225 chunkqueue_append_mem(&hctx->wb, (const char *)&header, sizeof(header)); 226 hctx->wb_reqlen += (int)sizeof(header); 227 } 228 229 return HANDLER_GO_ON; 230 } 231 232 static handler_t fcgi_create_env(handler_ctx *hctx) { 233 FCGI_BeginRequestRecord beginRecord; 234 FCGI_Header header; 235 int request_id; 236 237 gw_host *host = hctx->host; 238 request_st * const r = hctx->r; 239 240 http_cgi_opts opts = { 241 (hctx->gw_mode == FCGI_AUTHORIZER), 242 host->break_scriptfilename_for_php, 243 host->docroot, 244 host->strip_request_uri 245 }; 246 247 size_t rsz = (size_t)(r->read_queue.bytes_out - hctx->wb.bytes_in); 248 if (rsz >= 65536) rsz = r->rqst_header_len; 249 buffer * const b = chunkqueue_prepend_buffer_open_sz(&hctx->wb, rsz); 250 251 /* send FCGI_BEGIN_REQUEST */ 252 253 if (hctx->request_id == 0) { 254 hctx->request_id = 1; /* always use id 1 as we don't use multiplexing */ 255 } else { 256 log_error(r->conf.errh, __FILE__, __LINE__, 257 "fcgi-request is already in use: %d", hctx->request_id); 258 } 259 request_id = hctx->request_id; 260 261 fcgi_header(&(beginRecord.header), FCGI_BEGIN_REQUEST, request_id, sizeof(beginRecord.body), 0); 262 beginRecord.body.roleB0 = hctx->gw_mode; 263 beginRecord.body.roleB1 = 0; 264 beginRecord.body.flags = 0; 265 memset(beginRecord.body.reserved, 0, sizeof(beginRecord.body.reserved)); 266 fcgi_header(&header, FCGI_PARAMS, request_id, 0, 0); /*(set aside space to fill in later)*/ 267 buffer_append_str2(b, (const char *)&beginRecord, sizeof(beginRecord), 268 (const char *)&header, sizeof(header)); 269 270 /* send FCGI_PARAMS */ 271 272 if (0 != http_cgi_headers(r, &opts, fcgi_env_add, b)) { 273 r->http_status = 400; 274 r->handler_module = NULL; 275 buffer_clear(b); 276 chunkqueue_remove_finished_chunks(&hctx->wb); 277 return HANDLER_FINISHED; 278 } else { 279 fcgi_header(&(header), FCGI_PARAMS, request_id, 280 buffer_clen(b) - sizeof(FCGI_BeginRequestRecord) - sizeof(FCGI_Header), 0); 281 memcpy(b->ptr+sizeof(FCGI_BeginRequestRecord), (const char *)&header, sizeof(header)); 282 283 fcgi_header(&(header), FCGI_PARAMS, request_id, 0, 0); 284 buffer_append_string_len(b, (const char *)&header, sizeof(header)); 285 286 hctx->wb_reqlen = buffer_clen(b); 287 chunkqueue_prepend_buffer_commit(&hctx->wb); 288 } 289 290 if (r->reqbody_length) { 291 /*chunkqueue_append_chunkqueue(&hctx->wb, &r->reqbody_queue);*/ 292 if (r->reqbody_length > 0) 293 hctx->wb_reqlen += r->reqbody_length;/* (eventual) (minimal) total request size, not necessarily including all fcgi_headers around content length yet */ 294 else /* as-yet-unknown total request size (Transfer-Encoding: chunked)*/ 295 hctx->wb_reqlen = -hctx->wb_reqlen; 296 } 297 fcgi_stdin_append(hctx); 298 299 status_counter_inc(CONST_STR_LEN("fastcgi.requests")); 300 return HANDLER_GO_ON; 301 } 302 303 typedef struct { 304 unsigned int len; 305 int type; 306 int padding; 307 int request_id; 308 } fastcgi_response_packet; 309 310 static int fastcgi_get_packet(handler_ctx *hctx, fastcgi_response_packet *packet) { 311 FCGI_Header header; 312 off_t rblen = chunkqueue_length(hctx->rb); 313 if (rblen < (off_t)sizeof(FCGI_Header)) { 314 /* no header */ 315 if (hctx->conf.debug && 0 != rblen) { 316 log_error(hctx->r->conf.errh, __FILE__, __LINE__, 317 "FastCGI: header too small: %lld bytes < %zu bytes, " 318 "waiting for more data", (long long)rblen, sizeof(FCGI_Header)); 319 } 320 return -1; 321 } 322 char *ptr = (char *)&header; 323 uint32_t rd = sizeof(FCGI_Header); 324 if (chunkqueue_peek_data(hctx->rb, &ptr, &rd, hctx->r->conf.errh) < 0) 325 return -1; 326 if (rd != sizeof(FCGI_Header)) 327 return -1; 328 if (ptr != (char *)&header) /* copy into aligned struct */ 329 memcpy(&header, ptr, sizeof(FCGI_Header)); 330 331 /* we have at least a header, now check how much we have to fetch */ 332 packet->len = (header.contentLengthB0 | (header.contentLengthB1 << 8)) + header.paddingLength; 333 packet->request_id = (header.requestIdB0 | (header.requestIdB1 << 8)); 334 packet->type = header.type; 335 packet->padding = header.paddingLength; 336 337 if (packet->len > (unsigned int)rblen-sizeof(FCGI_Header)) { 338 return -1; /* we didn't get the full packet */ 339 } 340 341 chunkqueue_mark_written(hctx->rb, sizeof(FCGI_Header)); 342 return 0; 343 } 344 345 static void fastcgi_get_packet_body(buffer * const b, handler_ctx * const hctx, const fastcgi_response_packet * const packet) { 346 /* copy content; hctx->rb must contain at least packet->len content */ 347 /* (read entire packet and then truncate padding, if present) */ 348 const uint32_t blen = buffer_clen(b); 349 if (chunkqueue_read_data(hctx->rb, 350 buffer_string_prepare_append(b, packet->len), 351 packet->len, hctx->r->conf.errh) < 0) 352 return; /*(should not happen; should all be in memory)*/ 353 buffer_truncate(b, blen + packet->len - packet->padding); 354 } 355 356 __attribute_cold__ 357 static handler_t fcgi_recv_0(const request_st * const r, const handler_ctx * const hctx) { 358 if (-1 == hctx->request_id) return HANDLER_FINISHED; /*(flag request ended)*/ 359 if (!(fdevent_fdnode_interest(hctx->fdn) & FDEVENT_IN) 360 && !(r->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_POLLRDHUP)) 361 return HANDLER_GO_ON; 362 log_error(r->conf.errh, __FILE__, __LINE__, 363 "unexpected end-of-file (perhaps the fastcgi process died):" 364 "pid: %d socket: %s", 365 hctx->proc->pid, hctx->proc->connection_name->ptr); 366 367 return HANDLER_ERROR; 368 } 369 370 static handler_t fcgi_recv_parse_loop(request_st * const r, handler_ctx * const hctx) { 371 /* 372 * parse the fastcgi packets and forward the content to the write-queue 373 * 374 */ 375 fastcgi_response_packet packet; 376 int fin = 0; 377 do { 378 /* check if we have at least one packet */ 379 if (0 != fastcgi_get_packet(hctx, &packet)) { 380 /* no full packet */ 381 break; 382 } 383 384 switch(packet.type) { 385 case FCGI_STDOUT: 386 if (packet.len == 0) break; 387 388 /* is the header already finished */ 389 if (0 == r->resp_body_started) { 390 /* split header from body */ 391 buffer *hdrs = hctx->response; 392 if (NULL == hdrs) { 393 hdrs = r->tmp_buf; 394 buffer_clear(hdrs); 395 } 396 fastcgi_get_packet_body(hdrs, hctx, &packet); 397 if (HANDLER_GO_ON != http_response_parse_headers(r, &hctx->opts, hdrs)) { 398 hctx->send_content_body = 0; 399 fin = 1; 400 break; 401 } 402 if (0 == r->resp_body_started) { 403 if (!hctx->response) { 404 hctx->response = chunk_buffer_acquire(); 405 buffer_copy_buffer(hctx->response, hdrs); 406 } 407 } 408 else if (hctx->gw_mode == GW_AUTHORIZER && 409 (r->http_status == 0 || r->http_status == 200)) { 410 /* authorizer approved request; ignore the content here */ 411 hctx->send_content_body = 0; 412 hctx->opts.authorizer |= /*(save response streaming flags)*/ 413 (r->conf.stream_response_body 414 & (FDEVENT_STREAM_RESPONSE 415 |FDEVENT_STREAM_RESPONSE_BUFMIN)) << 1; 416 r->conf.stream_response_body &= 417 ~(FDEVENT_STREAM_RESPONSE|FDEVENT_STREAM_RESPONSE_BUFMIN); 418 } 419 #if 0 420 else if ((r->conf.stream_response_body & 421 (FDEVENT_STREAM_RESPONSE|FDEVENT_STREAM_RESPONSE_BUFMIN)) 422 && ( r->http_status == 204 423 || r->http_status == 205 424 || r->http_status == 304 425 || r->http_method == HTTP_METHOD_HEAD)) { 426 /* disable streaming to wait for backend protocol to signal 427 * end of response (prevent http_response_write_prepare() 428 * from short-circuiting and finishing responses without 429 * response body) */ 430 r->conf.stream_response_body &= 431 ~(FDEVENT_STREAM_RESPONSE|FDEVENT_STREAM_RESPONSE_BUFMIN); 432 } 433 #endif 434 } else if (hctx->send_content_body) { 435 if (0 != http_response_transfer_cqlen(r, hctx->rb, (size_t)(packet.len - packet.padding))) { 436 /* error writing to tempfile; 437 * truncate response or send 500 if nothing sent yet */ 438 hctx->send_content_body = 0; 439 fin = 1; 440 } 441 if (packet.padding) chunkqueue_mark_written(hctx->rb, packet.padding); 442 } else { 443 chunkqueue_mark_written(hctx->rb, packet.len); 444 } 445 break; 446 case FCGI_STDERR: 447 if (packet.len) { 448 buffer * const tb = r->tmp_buf; 449 buffer_clear(tb); 450 fastcgi_get_packet_body(tb, hctx, &packet); 451 log_error_multiline(r->conf.errh, __FILE__, __LINE__, 452 BUF_PTR_LEN(tb), "FastCGI-stderr:"); 453 } 454 break; 455 case FCGI_END_REQUEST: 456 hctx->request_id = -1; /*(flag request ended)*/ 457 fin = 1; 458 break; 459 default: 460 log_error(r->conf.errh, __FILE__, __LINE__, 461 "FastCGI: header.type not handled: %d", packet.type); 462 chunkqueue_mark_written(hctx->rb, packet.len); 463 break; 464 } 465 } while (0 == fin); 466 467 return 0 == fin ? HANDLER_GO_ON : HANDLER_FINISHED; 468 } 469 470 static handler_t fcgi_recv_parse(request_st * const r, struct http_response_opts_t *opts, buffer *b, size_t n) { 471 handler_ctx * const hctx = (handler_ctx *)opts->pdata; 472 if (0 == n) return fcgi_recv_0(r, hctx); 473 chunkqueue_append_buffer(hctx->rb, b); 474 return fcgi_recv_parse_loop(r, hctx); 475 } 476 477 static handler_t fcgi_check_extension(request_st * const r, void *p_d, int uri_path_handler) { 478 plugin_data *p = p_d; 479 handler_t rc; 480 481 if (NULL != r->handler_module) return HANDLER_GO_ON; 482 483 mod_fastcgi_patch_config(r, p); 484 if (NULL == p->conf.exts) return HANDLER_GO_ON; 485 486 rc = gw_check_extension(r, p, uri_path_handler, 0); 487 if (HANDLER_GO_ON != rc) return rc; 488 489 if (r->handler_module == p->self) { 490 handler_ctx *hctx = r->plugin_ctx[p->id]; 491 hctx->opts.backend = BACKEND_FASTCGI; 492 hctx->opts.parse = fcgi_recv_parse; 493 hctx->opts.pdata = hctx; /*(skip +255 for potential padding)*/ 494 hctx->opts.max_per_read = sizeof(FCGI_Header)+FCGI_MAX_LENGTH+1; 495 hctx->stdin_append = fcgi_stdin_append; 496 hctx->create_env = fcgi_create_env; 497 if (!hctx->rb) { 498 hctx->rb = chunkqueue_init(NULL); 499 } 500 else { 501 chunkqueue_reset(hctx->rb); 502 } 503 } 504 505 return HANDLER_GO_ON; 506 } 507 508 /* uri-path handler */ 509 static handler_t fcgi_check_extension_1(request_st * const r, void *p_d) { 510 return fcgi_check_extension(r, p_d, 1); 511 } 512 513 /* start request handler */ 514 static handler_t fcgi_check_extension_2(request_st * const r, void *p_d) { 515 return fcgi_check_extension(r, p_d, 0); 516 } 517 518 519 int mod_fastcgi_plugin_init(plugin *p); 520 int mod_fastcgi_plugin_init(plugin *p) { 521 p->version = LIGHTTPD_VERSION_ID; 522 p->name = "fastcgi"; 523 524 p->init = gw_init; 525 p->cleanup = gw_free; 526 p->set_defaults = mod_fastcgi_set_defaults; 527 p->handle_request_reset = gw_handle_request_reset; 528 p->handle_uri_clean = fcgi_check_extension_1; 529 p->handle_subrequest_start = fcgi_check_extension_2; 530 p->handle_subrequest = gw_handle_subrequest; 531 p->handle_trigger = gw_handle_trigger; 532 p->handle_waitpid = gw_handle_waitpid_cb; 533 534 return 0; 535 } 536