1 /* $OpenBSD: server_fcgi.c,v 1.88 2021/05/20 15:12:10 florian 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 59 struct 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 68 struct fcgi_begin_request_body { 69 uint16_t role; 70 uint8_t flags; 71 uint8_t reserved[5]; 72 } __packed; 73 74 struct server_fcgi_param { 75 int total_len; 76 uint8_t buf[FCGI_RECORD_SIZE]; 77 }; 78 79 int server_fcgi_header(struct client *, unsigned int); 80 void server_fcgi_read(struct bufferevent *, void *); 81 int server_fcgi_writeheader(struct client *, struct kv *, void *); 82 int server_fcgi_writechunk(struct client *); 83 int server_fcgi_getheaders(struct client *); 84 int fcgi_add_param(struct server_fcgi_param *, const char *, const char *, 85 struct client *); 86 87 int 88 server_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 403 int 404 fcgi_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 428 int 429 fcgi_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 484 void 485 server_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 if (server_fcgi_writechunk(clt) == -1) { 563 server_abort_http(clt, 500, 564 "encoding error"); 565 return; 566 } 567 break; 568 } 569 evbuffer_drain(clt->clt_srvevb, 570 EVBUFFER_LENGTH(clt->clt_srvevb)); 571 if (!clt->clt_fcgi.padding_len) { 572 clt->clt_fcgi.state = FCGI_READ_HEADER; 573 clt->clt_fcgi.toread = 574 sizeof(struct fcgi_record_header); 575 } else { 576 clt->clt_fcgi.state = FCGI_READ_PADDING; 577 clt->clt_fcgi.toread = 578 clt->clt_fcgi.padding_len; 579 } 580 break; 581 case FCGI_READ_PADDING: 582 evbuffer_drain(clt->clt_srvevb, 583 EVBUFFER_LENGTH(clt->clt_srvevb)); 584 clt->clt_fcgi.state = FCGI_READ_HEADER; 585 clt->clt_fcgi.toread = 586 sizeof(struct fcgi_record_header); 587 break; 588 } 589 } while (len > 0); 590 } 591 592 int 593 server_fcgi_header(struct client *clt, unsigned int code) 594 { 595 struct server_config *srv_conf = clt->clt_srv_conf; 596 struct http_descriptor *desc = clt->clt_descreq; 597 struct http_descriptor *resp = clt->clt_descresp; 598 const char *error; 599 char tmbuf[32]; 600 struct kv *kv, *cl, key; 601 602 clt->clt_fcgi.headerssent = 1; 603 604 if (desc == NULL || (error = server_httperror_byid(code)) == NULL) 605 return (-1); 606 607 if (server_log_http(clt, code, 0) == -1) 608 return (-1); 609 610 /* Add error codes */ 611 if (kv_setkey(&resp->http_pathquery, "%u", code) == -1 || 612 kv_set(&resp->http_pathquery, "%s", error) == -1) 613 return (-1); 614 615 /* Add headers */ 616 if (kv_add(&resp->http_headers, "Server", HTTPD_SERVERNAME) == NULL) 617 return (-1); 618 619 if (clt->clt_fcgi.type == FCGI_END_REQUEST || 620 EVBUFFER_LENGTH(clt->clt_srvevb) == 0) { 621 /* Can't chunk encode an empty body. */ 622 clt->clt_fcgi.chunked = 0; 623 624 /* But then we need a Content-Length... */ 625 key.kv_key = "Content-Length"; 626 if ((kv = kv_find(&resp->http_headers, &key)) == NULL) { 627 if (kv_add(&resp->http_headers, 628 "Content-Length", "0") == NULL) 629 return (-1); 630 } 631 } 632 633 /* Set chunked encoding */ 634 if (clt->clt_fcgi.chunked) { 635 /* XXX Should we keep and handle Content-Length instead? */ 636 key.kv_key = "Content-Length"; 637 if ((kv = kv_find(&resp->http_headers, &key)) != NULL) 638 kv_delete(&resp->http_headers, kv); 639 640 /* 641 * XXX What if the FastCGI added some kind of Transfer-Encoding? 642 * XXX like gzip, deflate or even "chunked"? 643 */ 644 if (kv_add(&resp->http_headers, 645 "Transfer-Encoding", "chunked") == NULL) 646 return (-1); 647 } 648 649 /* Is it a persistent connection? */ 650 if (clt->clt_persist) { 651 if (kv_add(&resp->http_headers, 652 "Connection", "keep-alive") == NULL) 653 return (-1); 654 } else if (kv_add(&resp->http_headers, "Connection", "close") == NULL) 655 return (-1); 656 657 /* HSTS header */ 658 if (srv_conf->flags & SRVFLAG_SERVER_HSTS && 659 srv_conf->flags & SRVFLAG_TLS) { 660 if ((cl = 661 kv_add(&resp->http_headers, "Strict-Transport-Security", 662 NULL)) == NULL || 663 kv_set(cl, "max-age=%d%s%s", srv_conf->hsts_max_age, 664 srv_conf->hsts_flags & HSTSFLAG_SUBDOMAINS ? 665 "; includeSubDomains" : "", 666 srv_conf->hsts_flags & HSTSFLAG_PRELOAD ? 667 "; preload" : "") == -1) 668 return (-1); 669 } 670 671 /* Date header is mandatory and should be added as late as possible */ 672 key.kv_key = "Date"; 673 if (kv_find(&resp->http_headers, &key) == NULL && 674 (server_http_time(time(NULL), tmbuf, sizeof(tmbuf)) <= 0 || 675 kv_add(&resp->http_headers, "Date", tmbuf) == NULL)) 676 return (-1); 677 678 if (server_writeresponse_http(clt) == -1 || 679 server_bufferevent_print(clt, "\r\n") == -1 || 680 server_headers(clt, resp, server_writeheader_http, NULL) == -1 || 681 server_bufferevent_print(clt, "\r\n") == -1) 682 return (-1); 683 684 return (0); 685 } 686 687 int 688 server_fcgi_writeheader(struct client *clt, struct kv *hdr, void *arg) 689 { 690 struct server_fcgi_param *param = arg; 691 char *val, *name, *p; 692 const char *key; 693 int ret; 694 695 if (hdr->kv_flags & KV_FLAG_INVALID) 696 return (0); 697 698 /* The key might have been updated in the parent */ 699 if (hdr->kv_parent != NULL && hdr->kv_parent->kv_key != NULL) 700 key = hdr->kv_parent->kv_key; 701 else 702 key = hdr->kv_key; 703 704 val = hdr->kv_value; 705 706 if (strcasecmp(key, "Content-Length") == 0 || 707 strcasecmp(key, "Content-Type") == 0) { 708 if ((name = strdup(key)) == NULL) 709 return (-1); 710 } else { 711 if (asprintf(&name, "HTTP_%s", key) == -1) 712 return (-1); 713 } 714 715 /* 716 * RFC 7230 defines a header field-name as a "token" and a "token" 717 * is defined as one or more characters for which isalpha or 718 * isdigit is true plus a list of additional characters. 719 * According to RFC 3875 a CGI environment variable is created 720 * by converting all letters to upper case and replacing '-' 721 * with '_'. 722 */ 723 for (p = name; *p != '\0'; p++) { 724 if (isalpha((unsigned char)*p)) 725 *p = toupper((unsigned char)*p); 726 else if (!(*p == '!' || *p == '#' || *p == '$' || *p == '%' || 727 *p == '&' || *p == '\'' || *p == '*' || *p == '+' || 728 *p == '.' || *p == '^' || *p == '_' || *p == '`' || 729 *p == '|' || *p == '~' || isdigit((unsigned char)*p))) 730 *p = '_'; 731 } 732 733 ret = fcgi_add_param(param, name, val, clt); 734 free(name); 735 736 return (ret); 737 } 738 739 int 740 server_fcgi_writechunk(struct client *clt) 741 { 742 struct evbuffer *evb = clt->clt_srvevb; 743 size_t len; 744 745 if (clt->clt_fcgi.type == FCGI_END_REQUEST) { 746 len = 0; 747 } else 748 len = EVBUFFER_LENGTH(evb); 749 750 if (clt->clt_fcgi.chunked) { 751 /* If len is 0, make sure to write the end marker only once */ 752 if (len == 0 && clt->clt_fcgi.end++) 753 return (0); 754 if (server_bufferevent_printf(clt, "%zx\r\n", len) == -1 || 755 server_bufferevent_write_chunk(clt, evb, len) == -1 || 756 server_bufferevent_print(clt, "\r\n") == -1) 757 return (-1); 758 } else if (len) 759 return (server_bufferevent_write_buffer(clt, evb)); 760 761 return (0); 762 } 763 764 int 765 server_fcgi_getheaders(struct client *clt) 766 { 767 struct http_descriptor *resp = clt->clt_descresp; 768 struct evbuffer *evb = clt->clt_srvevb; 769 int code, ret; 770 char *line, *key, *value; 771 const char *errstr; 772 773 while ((line = evbuffer_getline(evb)) != NULL && *line != '\0') { 774 key = line; 775 776 if ((value = strchr(key, ':')) == NULL) 777 break; 778 779 *value++ = '\0'; 780 value += strspn(value, " \t"); 781 782 DPRINTF("%s: %s: %s", __func__, key, value); 783 784 if (strcasecmp("Status", key) == 0) { 785 value[strcspn(value, " \t")] = '\0'; 786 code = (int)strtonum(value, 100, 600, &errstr); 787 if (errstr != NULL || server_httperror_byid( 788 code) == NULL) 789 code = 200; 790 clt->clt_fcgi.status = code; 791 } else { 792 (void)kv_add(&resp->http_headers, key, value); 793 } 794 free(line); 795 } 796 797 ret = (line != NULL && *line == '\0'); 798 799 free(line); 800 return ret; 801 } 802