server_fcgi.c revision 1.89
1/* $OpenBSD: server_fcgi.c,v 1.89 2021/10/23 15:52:44 benno Exp $ */ 2 3/* 4 * Copyright (c) 2014 Florian Obser <florian@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19#include <sys/types.h> 20#include <sys/time.h> 21#include <sys/socket.h> 22#include <sys/un.h> 23 24#include <netinet/in.h> 25#include <arpa/inet.h> 26 27#include <limits.h> 28#include <errno.h> 29#include <stdlib.h> 30#include <string.h> 31#include <stdio.h> 32#include <time.h> 33#include <ctype.h> 34#include <event.h> 35#include <unistd.h> 36 37#include "httpd.h" 38#include "http.h" 39 40#define FCGI_PADDING_SIZE 255 41#define FCGI_RECORD_SIZE \ 42 (sizeof(struct fcgi_record_header) + FCGI_CONTENT_SIZE + FCGI_PADDING_SIZE) 43 44#define FCGI_BEGIN_REQUEST 1 45#define FCGI_ABORT_REQUEST 2 46#define FCGI_END_REQUEST 3 47#define FCGI_PARAMS 4 48#define FCGI_STDIN 5 49#define FCGI_STDOUT 6 50#define FCGI_STDERR 7 51#define FCGI_DATA 8 52#define FCGI_GET_VALUES 9 53#define FCGI_GET_VALUES_RESULT 10 54#define FCGI_UNKNOWN_TYPE 11 55#define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE) 56 57#define FCGI_RESPONDER 1 58 59struct fcgi_record_header { 60 uint8_t version; 61 uint8_t type; 62 uint16_t id; 63 uint16_t content_len; 64 uint8_t padding_len; 65 uint8_t reserved; 66} __packed; 67 68struct fcgi_begin_request_body { 69 uint16_t role; 70 uint8_t flags; 71 uint8_t reserved[5]; 72} __packed; 73 74struct server_fcgi_param { 75 int total_len; 76 uint8_t buf[FCGI_RECORD_SIZE]; 77}; 78 79int server_fcgi_header(struct client *, unsigned int); 80void server_fcgi_read(struct bufferevent *, void *); 81int server_fcgi_writeheader(struct client *, struct kv *, void *); 82int server_fcgi_writechunk(struct client *); 83int server_fcgi_getheaders(struct client *); 84int fcgi_add_param(struct server_fcgi_param *, const char *, const char *, 85 struct client *); 86 87int 88server_fcgi(struct httpd *env, struct client *clt) 89{ 90 struct server_fcgi_param param; 91 struct server_config *srv_conf = clt->clt_srv_conf; 92 struct http_descriptor *desc = clt->clt_descreq; 93 struct fcgi_record_header *h; 94 struct fcgi_begin_request_body *begin; 95 struct fastcgi_param *fcgiparam; 96 char hbuf[HOST_NAME_MAX+1]; 97 size_t scriptlen; 98 int pathlen; 99 int fd = -1, ret; 100 const char *stripped, *alias, *errstr = NULL; 101 char *query_alias, *str, *script = NULL; 102 103 if ((fd = socket(srv_conf->fastcgi_ss.ss_family, 104 SOCK_STREAM | SOCK_NONBLOCK, 0)) == -1) 105 goto fail; 106 if ((connect(fd, (struct sockaddr *) &srv_conf->fastcgi_ss, 107 srv_conf->fastcgi_ss.ss_len)) == -1) { 108 if (errno != EINPROGRESS) 109 goto fail; 110 } 111 112 memset(hbuf, 0, sizeof(hbuf)); 113 clt->clt_fcgi.state = FCGI_READ_HEADER; 114 clt->clt_fcgi.toread = sizeof(struct fcgi_record_header); 115 clt->clt_fcgi.status = 200; 116 clt->clt_fcgi.headersdone = 0; 117 clt->clt_fcgi.headerssent = 0; 118 119 if (clt->clt_srvevb != NULL) 120 evbuffer_free(clt->clt_srvevb); 121 122 clt->clt_srvevb = evbuffer_new(); 123 if (clt->clt_srvevb == NULL) { 124 errstr = "failed to allocate evbuffer"; 125 goto fail; 126 } 127 128 close(clt->clt_fd); 129 clt->clt_fd = fd; 130 131 if (clt->clt_srvbev != NULL) 132 bufferevent_free(clt->clt_srvbev); 133 134 clt->clt_srvbev_throttled = 0; 135 clt->clt_srvbev = bufferevent_new(fd, server_fcgi_read, 136 NULL, server_file_error, clt); 137 if (clt->clt_srvbev == NULL) { 138 errstr = "failed to allocate fcgi buffer event"; 139 goto fail; 140 } 141 142 memset(¶m, 0, sizeof(param)); 143 144 h = (struct fcgi_record_header *)¶m.buf; 145 h->version = 1; 146 h->type = FCGI_BEGIN_REQUEST; 147 h->id = htons(1); 148 h->content_len = htons(sizeof(struct fcgi_begin_request_body)); 149 h->padding_len = 0; 150 151 begin = (struct fcgi_begin_request_body *)¶m.buf[sizeof(struct 152 fcgi_record_header)]; 153 begin->role = htons(FCGI_RESPONDER); 154 155 if (bufferevent_write(clt->clt_srvbev, ¶m.buf, 156 sizeof(struct fcgi_record_header) + 157 sizeof(struct fcgi_begin_request_body)) == -1) { 158 errstr = "failed to write to evbuffer"; 159 goto fail; 160 } 161 162 h->type = FCGI_PARAMS; 163 h->content_len = param.total_len = 0; 164 165 alias = desc->http_path_alias != NULL 166 ? desc->http_path_alias 167 : desc->http_path; 168 169 query_alias = desc->http_query_alias != NULL 170 ? desc->http_query_alias 171 : desc->http_query; 172 173 stripped = server_root_strip(alias, srv_conf->strip); 174 if ((pathlen = asprintf(&script, "%s%s", srv_conf->root, stripped)) 175 == -1) { 176 errstr = "failed to get script name"; 177 goto fail; 178 } 179 180 scriptlen = path_info(script); 181 /* 182 * no part of root should show up in PATH_INFO. 183 * therefore scriptlen should be >= strlen(root) 184 */ 185 if (scriptlen < strlen(srv_conf->root)) 186 scriptlen = strlen(srv_conf->root); 187 if ((int)scriptlen < pathlen) { 188 if (fcgi_add_param(¶m, "PATH_INFO", 189 script + scriptlen, clt) == -1) { 190 errstr = "failed to encode param"; 191 goto fail; 192 } 193 script[scriptlen] = '\0'; 194 } else { 195 /* RFC 3875 mandates that PATH_INFO is empty if not set */ 196 if (fcgi_add_param(¶m, "PATH_INFO", "", clt) == -1) { 197 errstr = "failed to encode param"; 198 goto fail; 199 } 200 } 201 202 /* 203 * calculate length of http SCRIPT_NAME: 204 * add length of stripped prefix, 205 * subtract length of prepended local root 206 */ 207 scriptlen += (stripped - alias) - strlen(srv_conf->root); 208 if ((str = strndup(alias, scriptlen)) == NULL) 209 goto fail; 210 ret = fcgi_add_param(¶m, "SCRIPT_NAME", str, clt); 211 free(str); 212 if (ret == -1) { 213 errstr = "failed to encode param"; 214 goto fail; 215 } 216 if (fcgi_add_param(¶m, "SCRIPT_FILENAME", server_root_strip(script, 217 srv_conf->fcgistrip), clt) == -1) { 218 errstr = "failed to encode param"; 219 goto fail; 220 } 221 222 if (query_alias) { 223 if (fcgi_add_param(¶m, "QUERY_STRING", query_alias, 224 clt) == -1) { 225 errstr = "failed to encode param"; 226 goto fail; 227 } 228 } else if (fcgi_add_param(¶m, "QUERY_STRING", "", clt) == -1) { 229 errstr = "failed to encode param"; 230 goto fail; 231 } 232 233 if (fcgi_add_param(¶m, "DOCUMENT_ROOT", server_root_strip( 234 srv_conf->root, srv_conf->fcgistrip), clt) == -1) { 235 errstr = "failed to encode param"; 236 goto fail; 237 } 238 if (fcgi_add_param(¶m, "DOCUMENT_URI", alias, 239 clt) == -1) { 240 errstr = "failed to encode param"; 241 goto fail; 242 } 243 if (fcgi_add_param(¶m, "GATEWAY_INTERFACE", "CGI/1.1", 244 clt) == -1) { 245 errstr = "failed to encode param"; 246 goto fail; 247 } 248 249 if (srv_conf->flags & SRVFLAG_AUTH) { 250 if (fcgi_add_param(¶m, "REMOTE_USER", 251 clt->clt_remote_user, clt) == -1) { 252 errstr = "failed to encode param"; 253 goto fail; 254 } 255 } 256 257 /* Add HTTP_* headers */ 258 if (server_headers(clt, desc, server_fcgi_writeheader, ¶m) == -1) { 259 errstr = "failed to encode param"; 260 goto fail; 261 } 262 263 if (srv_conf->flags & SRVFLAG_TLS) { 264 if (fcgi_add_param(¶m, "HTTPS", "on", clt) == -1) { 265 errstr = "failed to encode param"; 266 goto fail; 267 } 268 if (srv_conf->tls_flags != 0 && fcgi_add_param(¶m, 269 "TLS_PEER_VERIFY", printb_flags(srv_conf->tls_flags, 270 TLSFLAG_BITS), clt) == -1) { 271 errstr = "failed to encode param"; 272 goto fail; 273 } 274 } 275 276 TAILQ_FOREACH(fcgiparam, &srv_conf->fcgiparams, entry) { 277 if (fcgi_add_param(¶m, fcgiparam->name, fcgiparam->value, 278 clt) == -1) { 279 errstr = "failed to encode param"; 280 goto fail; 281 } 282 } 283 284 (void)print_host(&clt->clt_ss, hbuf, sizeof(hbuf)); 285 if (fcgi_add_param(¶m, "REMOTE_ADDR", hbuf, clt) == -1) { 286 errstr = "failed to encode param"; 287 goto fail; 288 } 289 290 (void)snprintf(hbuf, sizeof(hbuf), "%d", ntohs(clt->clt_port)); 291 if (fcgi_add_param(¶m, "REMOTE_PORT", hbuf, clt) == -1) { 292 errstr = "failed to encode param"; 293 goto fail; 294 } 295 296 if (fcgi_add_param(¶m, "REQUEST_METHOD", 297 server_httpmethod_byid(desc->http_method), clt) == -1) { 298 errstr = "failed to encode param"; 299 goto fail; 300 } 301 302 if (!desc->http_query) { 303 if (fcgi_add_param(¶m, "REQUEST_URI", desc->http_path_orig, 304 clt) == -1) { 305 errstr = "failed to encode param"; 306 goto fail; 307 } 308 } else { 309 if (asprintf(&str, "%s?%s", desc->http_path_orig, 310 desc->http_query) == -1) { 311 errstr = "failed to encode param"; 312 goto fail; 313 } 314 ret = fcgi_add_param(¶m, "REQUEST_URI", str, clt); 315 free(str); 316 if (ret == -1) { 317 errstr = "failed to encode param"; 318 goto fail; 319 } 320 } 321 322 (void)print_host(&clt->clt_srv_ss, hbuf, sizeof(hbuf)); 323 if (fcgi_add_param(¶m, "SERVER_ADDR", hbuf, clt) == -1) { 324 errstr = "failed to encode param"; 325 goto fail; 326 } 327 328 (void)snprintf(hbuf, sizeof(hbuf), "%d", 329 ntohs(server_socket_getport(&clt->clt_srv_ss))); 330 if (fcgi_add_param(¶m, "SERVER_PORT", hbuf, clt) == -1) { 331 errstr = "failed to encode param"; 332 goto fail; 333 } 334 335 if (fcgi_add_param(¶m, "SERVER_NAME", srv_conf->name, 336 clt) == -1) { 337 errstr = "failed to encode param"; 338 goto fail; 339 } 340 341 if (fcgi_add_param(¶m, "SERVER_PROTOCOL", desc->http_version, 342 clt) == -1) { 343 errstr = "failed to encode param"; 344 goto fail; 345 } 346 347 if (fcgi_add_param(¶m, "SERVER_SOFTWARE", HTTPD_SERVERNAME, 348 clt) == -1) { 349 errstr = "failed to encode param"; 350 goto fail; 351 } 352 353 if (param.total_len != 0) { /* send last params record */ 354 if (bufferevent_write(clt->clt_srvbev, ¶m.buf, 355 sizeof(struct fcgi_record_header) + 356 ntohs(h->content_len)) == -1) { 357 errstr = "failed to write to client evbuffer"; 358 goto fail; 359 } 360 } 361 362 /* send "no more params" message */ 363 h->content_len = 0; 364 if (bufferevent_write(clt->clt_srvbev, ¶m.buf, 365 sizeof(struct fcgi_record_header)) == -1) { 366 errstr = "failed to write to client evbuffer"; 367 goto fail; 368 } 369 370 bufferevent_settimeout(clt->clt_srvbev, 371 srv_conf->timeout.tv_sec, srv_conf->timeout.tv_sec); 372 bufferevent_enable(clt->clt_srvbev, EV_READ|EV_WRITE); 373 if (clt->clt_toread != 0) { 374 server_read_httpcontent(clt->clt_bev, clt); 375 bufferevent_enable(clt->clt_bev, EV_READ); 376 } else { 377 bufferevent_disable(clt->clt_bev, EV_READ); 378 fcgi_add_stdin(clt, NULL); 379 } 380 381 if (strcmp(desc->http_version, "HTTP/1.1") == 0) { 382 clt->clt_fcgi.chunked = 1; 383 } else { 384 /* HTTP/1.0 does not support chunked encoding */ 385 clt->clt_fcgi.chunked = 0; 386 clt->clt_persist = 0; 387 } 388 clt->clt_fcgi.end = 0; 389 clt->clt_done = 0; 390 391 free(script); 392 return (0); 393 fail: 394 free(script); 395 if (errstr == NULL) 396 errstr = strerror(errno); 397 if (fd != -1 && clt->clt_fd != fd) 398 close(fd); 399 server_abort_http(clt, 500, errstr); 400 return (-1); 401} 402 403int 404fcgi_add_stdin(struct client *clt, struct evbuffer *evbuf) 405{ 406 struct fcgi_record_header h; 407 408 memset(&h, 0, sizeof(h)); 409 h.version = 1; 410 h.type = FCGI_STDIN; 411 h.id = htons(1); 412 h.padding_len = 0; 413 414 if (evbuf == NULL) { 415 h.content_len = 0; 416 return bufferevent_write(clt->clt_srvbev, &h, 417 sizeof(struct fcgi_record_header)); 418 } else { 419 h.content_len = htons(EVBUFFER_LENGTH(evbuf)); 420 if (bufferevent_write(clt->clt_srvbev, &h, 421 sizeof(struct fcgi_record_header)) == -1) 422 return -1; 423 return bufferevent_write_buffer(clt->clt_srvbev, evbuf); 424 } 425 return (0); 426} 427 428int 429fcgi_add_param(struct server_fcgi_param *p, const char *key, 430 const char *val, struct client *clt) 431{ 432 struct fcgi_record_header *h; 433 int len = 0; 434 int key_len = strlen(key); 435 int val_len = strlen(val); 436 uint8_t *param; 437 438 len += key_len + val_len; 439 len += key_len > 127 ? 4 : 1; 440 len += val_len > 127 ? 4 : 1; 441 442 DPRINTF("%s: %s[%d] => %s[%d], total_len: %d", __func__, key, key_len, 443 val, val_len, p->total_len); 444 445 if (len > FCGI_CONTENT_SIZE) 446 return (-1); 447 448 if (p->total_len + len > FCGI_CONTENT_SIZE) { 449 if (bufferevent_write(clt->clt_srvbev, p->buf, 450 sizeof(struct fcgi_record_header) + p->total_len) == -1) 451 return (-1); 452 p->total_len = 0; 453 } 454 455 h = (struct fcgi_record_header *)p->buf; 456 param = p->buf + sizeof(*h) + p->total_len; 457 458 if (key_len > 127) { 459 *param++ = ((key_len >> 24) & 0xff) | 0x80; 460 *param++ = ((key_len >> 16) & 0xff); 461 *param++ = ((key_len >> 8) & 0xff); 462 *param++ = (key_len & 0xff); 463 } else 464 *param++ = key_len; 465 466 if (val_len > 127) { 467 *param++ = ((val_len >> 24) & 0xff) | 0x80; 468 *param++ = ((val_len >> 16) & 0xff); 469 *param++ = ((val_len >> 8) & 0xff); 470 *param++ = (val_len & 0xff); 471 } else 472 *param++ = val_len; 473 474 memcpy(param, key, key_len); 475 param += key_len; 476 memcpy(param, val, val_len); 477 478 p->total_len += len; 479 480 h->content_len = htons(p->total_len); 481 return (0); 482} 483 484void 485server_fcgi_read(struct bufferevent *bev, void *arg) 486{ 487 uint8_t buf[FCGI_RECORD_SIZE]; 488 struct client *clt = (struct client *) arg; 489 struct fcgi_record_header *h; 490 size_t len; 491 char *ptr; 492 493 do { 494 len = bufferevent_read(bev, buf, clt->clt_fcgi.toread); 495 if (evbuffer_add(clt->clt_srvevb, buf, len) == -1) { 496 server_abort_http(clt, 500, "short write"); 497 return; 498 } 499 clt->clt_fcgi.toread -= len; 500 DPRINTF("%s: len: %lu toread: %d state: %d type: %d", 501 __func__, len, clt->clt_fcgi.toread, 502 clt->clt_fcgi.state, clt->clt_fcgi.type); 503 504 if (clt->clt_fcgi.toread != 0) 505 return; 506 507 switch (clt->clt_fcgi.state) { 508 case FCGI_READ_HEADER: 509 clt->clt_fcgi.state = FCGI_READ_CONTENT; 510 h = (struct fcgi_record_header *) 511 EVBUFFER_DATA(clt->clt_srvevb); 512 DPRINTF("%s: record header: version %d type %d id %d " 513 "content len %d padding %d", __func__, 514 h->version, h->type, ntohs(h->id), 515 ntohs(h->content_len), h->padding_len); 516 clt->clt_fcgi.type = h->type; 517 clt->clt_fcgi.toread = ntohs(h->content_len); 518 clt->clt_fcgi.padding_len = h->padding_len; 519 evbuffer_drain(clt->clt_srvevb, 520 EVBUFFER_LENGTH(clt->clt_srvevb)); 521 if (clt->clt_fcgi.toread != 0) 522 break; 523 else if (clt->clt_fcgi.type == FCGI_STDOUT && 524 !clt->clt_chunk) { 525 server_abort_http(clt, 500, "empty stdout"); 526 return; 527 } 528 529 /* fallthrough if content_len == 0 */ 530 case FCGI_READ_CONTENT: 531 switch (clt->clt_fcgi.type) { 532 case FCGI_STDERR: 533 if (EVBUFFER_LENGTH(clt->clt_srvevb) > 0 && 534 (ptr = get_string( 535 EVBUFFER_DATA(clt->clt_srvevb), 536 EVBUFFER_LENGTH(clt->clt_srvevb))) 537 != NULL) { 538 server_sendlog(clt->clt_srv_conf, 539 IMSG_LOG_ERROR, "%s", ptr); 540 free(ptr); 541 } 542 break; 543 case FCGI_STDOUT: 544 ++clt->clt_chunk; 545 if (!clt->clt_fcgi.headersdone) { 546 clt->clt_fcgi.headersdone = 547 server_fcgi_getheaders(clt); 548 if (!EVBUFFER_LENGTH(clt->clt_srvevb)) 549 break; 550 } 551 /* FALLTHROUGH */ 552 case FCGI_END_REQUEST: 553 if (clt->clt_fcgi.headersdone && 554 !clt->clt_fcgi.headerssent) { 555 if (server_fcgi_header(clt, 556 clt->clt_fcgi.status) == -1) { 557 server_abort_http(clt, 500, 558 "malformed fcgi headers"); 559 return; 560 } 561 } 562 /* Don't send content for HEAD requests */ 563 if (clt->clt_fcgi.headerssent && 564 ((struct http_descriptor *) 565 clt->clt_descreq)->http_method 566 == HTTP_METHOD_HEAD) 567 return; 568 if (server_fcgi_writechunk(clt) == -1) { 569 server_abort_http(clt, 500, 570 "encoding error"); 571 return; 572 } 573 break; 574 } 575 evbuffer_drain(clt->clt_srvevb, 576 EVBUFFER_LENGTH(clt->clt_srvevb)); 577 if (!clt->clt_fcgi.padding_len) { 578 clt->clt_fcgi.state = FCGI_READ_HEADER; 579 clt->clt_fcgi.toread = 580 sizeof(struct fcgi_record_header); 581 } else { 582 clt->clt_fcgi.state = FCGI_READ_PADDING; 583 clt->clt_fcgi.toread = 584 clt->clt_fcgi.padding_len; 585 } 586 break; 587 case FCGI_READ_PADDING: 588 evbuffer_drain(clt->clt_srvevb, 589 EVBUFFER_LENGTH(clt->clt_srvevb)); 590 clt->clt_fcgi.state = FCGI_READ_HEADER; 591 clt->clt_fcgi.toread = 592 sizeof(struct fcgi_record_header); 593 break; 594 } 595 } while (len > 0); 596} 597 598int 599server_fcgi_header(struct client *clt, unsigned int code) 600{ 601 struct server_config *srv_conf = clt->clt_srv_conf; 602 struct http_descriptor *desc = clt->clt_descreq; 603 struct http_descriptor *resp = clt->clt_descresp; 604 const char *error; 605 char tmbuf[32]; 606 struct kv *kv, *cl, key; 607 608 clt->clt_fcgi.headerssent = 1; 609 610 if (desc == NULL || (error = server_httperror_byid(code)) == NULL) 611 return (-1); 612 613 if (server_log_http(clt, code, 0) == -1) 614 return (-1); 615 616 /* Add error codes */ 617 if (kv_setkey(&resp->http_pathquery, "%u", code) == -1 || 618 kv_set(&resp->http_pathquery, "%s", error) == -1) 619 return (-1); 620 621 /* Add headers */ 622 if (kv_add(&resp->http_headers, "Server", HTTPD_SERVERNAME) == NULL) 623 return (-1); 624 625 if (clt->clt_fcgi.type == FCGI_END_REQUEST || 626 EVBUFFER_LENGTH(clt->clt_srvevb) == 0) { 627 /* Can't chunk encode an empty body. */ 628 clt->clt_fcgi.chunked = 0; 629 630 /* But then we need a Content-Length unless method is HEAD... */ 631 if (desc->http_method != HTTP_METHOD_HEAD) { 632 key.kv_key = "Content-Length"; 633 if ((kv = kv_find(&resp->http_headers, &key)) == NULL) { 634 if (kv_add(&resp->http_headers, 635 "Content-Length", "0") == NULL) 636 return (-1); 637 } 638 } 639 } 640 641 /* Send chunked encoding header */ 642 if (clt->clt_fcgi.chunked) { 643 /* but only if no Content-Length header is supplied */ 644 key.kv_key = "Content-Length"; 645 if ((kv = kv_find(&resp->http_headers, &key)) != NULL) { 646 clt->clt_fcgi.chunked = 0; 647 } else { 648 /* 649 * XXX What if the FastCGI added some kind of 650 * Transfer-Encoding, like gzip, deflate or even 651 * "chunked"? 652 */ 653 if (kv_add(&resp->http_headers, 654 "Transfer-Encoding", "chunked") == NULL) 655 return (-1); 656 } 657 } 658 659 /* Is it a persistent connection? */ 660 if (clt->clt_persist) { 661 if (kv_add(&resp->http_headers, 662 "Connection", "keep-alive") == NULL) 663 return (-1); 664 } else if (kv_add(&resp->http_headers, "Connection", "close") == NULL) 665 return (-1); 666 667 /* HSTS header */ 668 if (srv_conf->flags & SRVFLAG_SERVER_HSTS && 669 srv_conf->flags & SRVFLAG_TLS) { 670 if ((cl = 671 kv_add(&resp->http_headers, "Strict-Transport-Security", 672 NULL)) == NULL || 673 kv_set(cl, "max-age=%d%s%s", srv_conf->hsts_max_age, 674 srv_conf->hsts_flags & HSTSFLAG_SUBDOMAINS ? 675 "; includeSubDomains" : "", 676 srv_conf->hsts_flags & HSTSFLAG_PRELOAD ? 677 "; preload" : "") == -1) 678 return (-1); 679 } 680 681 /* Date header is mandatory and should be added as late as possible */ 682 key.kv_key = "Date"; 683 if (kv_find(&resp->http_headers, &key) == NULL && 684 (server_http_time(time(NULL), tmbuf, sizeof(tmbuf)) <= 0 || 685 kv_add(&resp->http_headers, "Date", tmbuf) == NULL)) 686 return (-1); 687 688 if (server_writeresponse_http(clt) == -1 || 689 server_bufferevent_print(clt, "\r\n") == -1 || 690 server_headers(clt, resp, server_writeheader_http, NULL) == -1 || 691 server_bufferevent_print(clt, "\r\n") == -1) 692 return (-1); 693 694 return (0); 695} 696 697int 698server_fcgi_writeheader(struct client *clt, struct kv *hdr, void *arg) 699{ 700 struct server_fcgi_param *param = arg; 701 char *val, *name, *p; 702 const char *key; 703 int ret; 704 705 if (hdr->kv_flags & KV_FLAG_INVALID) 706 return (0); 707 708 /* The key might have been updated in the parent */ 709 if (hdr->kv_parent != NULL && hdr->kv_parent->kv_key != NULL) 710 key = hdr->kv_parent->kv_key; 711 else 712 key = hdr->kv_key; 713 714 val = hdr->kv_value; 715 716 if (strcasecmp(key, "Content-Length") == 0 || 717 strcasecmp(key, "Content-Type") == 0) { 718 if ((name = strdup(key)) == NULL) 719 return (-1); 720 } else { 721 if (asprintf(&name, "HTTP_%s", key) == -1) 722 return (-1); 723 } 724 725 /* 726 * RFC 7230 defines a header field-name as a "token" and a "token" 727 * is defined as one or more characters for which isalpha or 728 * isdigit is true plus a list of additional characters. 729 * According to RFC 3875 a CGI environment variable is created 730 * by converting all letters to upper case and replacing '-' 731 * with '_'. 732 */ 733 for (p = name; *p != '\0'; p++) { 734 if (isalpha((unsigned char)*p)) 735 *p = toupper((unsigned char)*p); 736 else if (!(*p == '!' || *p == '#' || *p == '$' || *p == '%' || 737 *p == '&' || *p == '\'' || *p == '*' || *p == '+' || 738 *p == '.' || *p == '^' || *p == '_' || *p == '`' || 739 *p == '|' || *p == '~' || isdigit((unsigned char)*p))) 740 *p = '_'; 741 } 742 743 ret = fcgi_add_param(param, name, val, clt); 744 free(name); 745 746 return (ret); 747} 748 749int 750server_fcgi_writechunk(struct client *clt) 751{ 752 struct evbuffer *evb = clt->clt_srvevb; 753 size_t len; 754 755 if (clt->clt_fcgi.type == FCGI_END_REQUEST) { 756 len = 0; 757 } else 758 len = EVBUFFER_LENGTH(evb); 759 760 if (clt->clt_fcgi.chunked) { 761 /* If len is 0, make sure to write the end marker only once */ 762 if (len == 0 && clt->clt_fcgi.end++) 763 return (0); 764 if (server_bufferevent_printf(clt, "%zx\r\n", len) == -1 || 765 server_bufferevent_write_chunk(clt, evb, len) == -1 || 766 server_bufferevent_print(clt, "\r\n") == -1) 767 return (-1); 768 } else if (len) 769 return (server_bufferevent_write_buffer(clt, evb)); 770 771 return (0); 772} 773 774int 775server_fcgi_getheaders(struct client *clt) 776{ 777 struct http_descriptor *resp = clt->clt_descresp; 778 struct evbuffer *evb = clt->clt_srvevb; 779 int code, ret; 780 char *line, *key, *value; 781 const char *errstr; 782 783 while ((line = evbuffer_getline(evb)) != NULL && *line != '\0') { 784 key = line; 785 786 if ((value = strchr(key, ':')) == NULL) 787 break; 788 789 *value++ = '\0'; 790 value += strspn(value, " \t"); 791 792 DPRINTF("%s: %s: %s", __func__, key, value); 793 794 if (strcasecmp("Status", key) == 0) { 795 value[strcspn(value, " \t")] = '\0'; 796 code = (int)strtonum(value, 100, 600, &errstr); 797 if (errstr != NULL || server_httperror_byid( 798 code) == NULL) 799 code = 200; 800 clt->clt_fcgi.status = code; 801 } else { 802 (void)kv_add(&resp->http_headers, key, value); 803 } 804 free(line); 805 } 806 807 ret = (line != NULL && *line == '\0'); 808 809 free(line); 810 return ret; 811} 812