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