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