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