server_fcgi.c revision 1.22
1/* $OpenBSD: server_fcgi.c,v 1.22 2014/08/06 13:40:18 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/queue.h> 21#include <sys/time.h> 22#include <sys/stat.h> 23#include <sys/socket.h> 24#include <sys/un.h> 25#include <sys/tree.h> 26#include <sys/hash.h> 27 28#include <net/if.h> 29#include <netinet/in_systm.h> 30#include <netinet/in.h> 31#include <netinet/ip.h> 32#include <netinet/tcp.h> 33#include <arpa/inet.h> 34 35#include <errno.h> 36#include <fcntl.h> 37#include <stdlib.h> 38#include <string.h> 39#include <unistd.h> 40#include <stdio.h> 41#include <ctype.h> 42#include <err.h> 43#include <event.h> 44 45#include "httpd.h" 46#include "http.h" 47 48#define FCGI_CONTENT_SIZE 65535 49#define FCGI_PADDING_SIZE 255 50#define FCGI_RECORD_SIZE \ 51 (sizeof(struct fcgi_record_header) + FCGI_CONTENT_SIZE + FCGI_PADDING_SIZE) 52 53#define FCGI_BEGIN_REQUEST 1 54#define FCGI_ABORT_REQUEST 2 55#define FCGI_END_REQUEST 3 56#define FCGI_PARAMS 4 57#define FCGI_STDIN 5 58#define FCGI_STDOUT 6 59#define FCGI_STDERR 7 60#define FCGI_DATA 8 61#define FCGI_GET_VALUES 9 62#define FCGI_GET_VALUES_RESULT 10 63#define FCGI_UNKNOWN_TYPE 11 64#define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE) 65 66#define FCGI_RESPONDER 1 67 68struct fcgi_record_header { 69 uint8_t version; 70 uint8_t type; 71 uint16_t id; 72 uint16_t content_len; 73 uint8_t padding_len; 74 uint8_t reserved; 75} __packed; 76 77struct fcgi_begin_request_body { 78 uint16_t role; 79 uint8_t flags; 80 uint8_t reserved[5]; 81} __packed; 82 83struct server_fcgi_param { 84 int total_len; 85 uint8_t buf[FCGI_RECORD_SIZE]; 86}; 87 88int server_fcgi_header(struct client *, u_int); 89void server_fcgi_read(struct bufferevent *, void *); 90int server_fcgi_writeheader(struct client *, struct kv *, void *); 91int fcgi_add_param(struct server_fcgi_param *, const char *, const char *, 92 struct client *); 93 94int 95server_fcgi(struct httpd *env, struct client *clt) 96{ 97 struct server_fcgi_param param; 98 char hbuf[MAXHOSTNAMELEN]; 99 struct server_config *srv_conf = clt->clt_srv_conf; 100 struct http_descriptor *desc = clt->clt_desc; 101 struct sockaddr_un sun; 102 struct fcgi_record_header *h; 103 struct fcgi_begin_request_body *begin; 104 size_t len; 105 ssize_t scriptlen; 106 int fd = -1, ret; 107 const char *errstr = NULL; 108 char *str, *p, *script = NULL; 109 in_port_t port; 110 struct sockaddr_storage ss; 111 112 if (srv_conf->socket[0] == ':') { 113 p = srv_conf->socket + 1; 114 115 port = strtonum(p, 0, 0xffff, &errstr); 116 if (errstr != NULL) { 117 log_warn("%s: strtonum %s, %s", __func__, p, errstr); 118 goto fail; 119 } 120 memset(&ss, 0, sizeof(ss)); 121 ss.ss_family = AF_INET; 122 ((struct sockaddr_in *) 123 &ss)->sin_addr.s_addr = htonl(INADDR_LOOPBACK); 124 port = htons(port); 125 126 if ((fd = server_socket_connect(&ss, port, srv_conf)) == -1) 127 goto fail; 128 } else { 129 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 130 goto fail; 131 132 memset(&sun, 0, sizeof(sun)); 133 sun.sun_family = AF_UNIX; 134 len = strlcpy(sun.sun_path, 135 srv_conf->socket, sizeof(sun.sun_path)); 136 if (len >= sizeof(sun.sun_path)) { 137 errstr = "socket path to long"; 138 goto fail; 139 } 140 sun.sun_len = len; 141 142 if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) 143 goto fail; 144 } 145 146 socket_set_blockmode(fd, BM_NONBLOCK); 147 148 memset(&hbuf, 0, sizeof(hbuf)); 149 clt->clt_fcgi_state = FCGI_READ_HEADER; 150 clt->clt_fcgi_toread = sizeof(struct fcgi_record_header); 151 152 if (clt->clt_srvevb != NULL) 153 evbuffer_free(clt->clt_srvevb); 154 155 clt->clt_srvevb = evbuffer_new(); 156 if (clt->clt_srvevb == NULL) { 157 errstr = "failed to allocate evbuffer"; 158 goto fail; 159 } 160 161 clt->clt_fd = fd; 162 if (clt->clt_srvbev != NULL) 163 bufferevent_free(clt->clt_srvbev); 164 165 clt->clt_srvbev = bufferevent_new(fd, server_fcgi_read, 166 NULL, server_file_error, clt); 167 if (clt->clt_srvbev == NULL) { 168 errstr = "failed to allocate fcgi buffer event"; 169 goto fail; 170 } 171 172 memset(¶m, 0, sizeof(param)); 173 174 h = (struct fcgi_record_header *)¶m.buf; 175 h->version = 1; 176 h->type = FCGI_BEGIN_REQUEST; 177 h->id = htons(1); 178 h->content_len = htons(sizeof(struct fcgi_begin_request_body)); 179 h->padding_len = 0; 180 181 begin = (struct fcgi_begin_request_body *)¶m.buf[sizeof(struct 182 fcgi_record_header)]; 183 begin->role = htons(FCGI_RESPONDER); 184 185 bufferevent_write(clt->clt_srvbev, ¶m.buf, 186 sizeof(struct fcgi_record_header) + 187 sizeof(struct fcgi_begin_request_body)); 188 189 h->type = FCGI_PARAMS; 190 h->content_len = param.total_len = 0; 191 192 if (asprintf(&script, "%s%s", srv_conf->root, 193 desc->http_path) == -1 || 194 (scriptlen = path_info(script)) == -1) { 195 errstr = "failed to get script name"; 196 goto fail; 197 } 198 199 if (scriptlen) { 200 if (fcgi_add_param(¶m, "PATH_INFO", 201 script + scriptlen, clt) == -1) { 202 errstr = "failed to encode param"; 203 goto fail; 204 } 205 script[scriptlen] = '\0'; 206 } 207 208 if (fcgi_add_param(¶m, "SCRIPT_NAME", 209 script + strlen(srv_conf->root), clt) == -1) { 210 errstr = "failed to encode param"; 211 goto fail; 212 } 213 if (fcgi_add_param(¶m, "SCRIPT_FILENAME", script, clt) == -1) { 214 errstr = "failed to encode param"; 215 goto fail; 216 } 217 218 if (desc->http_query) 219 if (fcgi_add_param(¶m, "QUERY_STRING", desc->http_query, 220 clt) == -1) { 221 errstr = "failed to encode param"; 222 goto fail; 223 } 224 225 if (fcgi_add_param(¶m, "DOCUMENT_ROOT", srv_conf->root, 226 clt) == -1) { 227 errstr = "failed to encode param"; 228 goto fail; 229 } 230 if (fcgi_add_param(¶m, "DOCUMENT_URI", desc->http_path, 231 clt) == -1) { 232 errstr = "failed to encode param"; 233 goto fail; 234 } 235 if (fcgi_add_param(¶m, "GATEWAY_INTERFACE", "CGI/1.1", 236 clt) == -1) { 237 errstr = "failed to encode param"; 238 goto fail; 239 } 240 241 /* Add HTTP_* headers */ 242 if (server_headers(clt, server_fcgi_writeheader, ¶m) == -1) { 243 errstr = "failed to encode param"; 244 goto fail; 245 } 246 247 if (srv_conf->flags & SRVFLAG_SSL) 248 if (fcgi_add_param(¶m, "HTTPS", "on", clt) == -1) { 249 errstr = "failed to encode param"; 250 goto fail; 251 } 252 253 (void)print_host(&clt->clt_ss, hbuf, sizeof(hbuf)); 254 if (fcgi_add_param(¶m, "REMOTE_ADDR", hbuf, clt) == -1) { 255 errstr = "failed to encode param"; 256 goto fail; 257 } 258 259 (void)snprintf(hbuf, sizeof(hbuf), "%d", ntohs(clt->clt_port)); 260 if (fcgi_add_param(¶m, "REMOTE_PORT", hbuf, clt) == -1) { 261 errstr = "failed to encode param"; 262 goto fail; 263 } 264 265 if (fcgi_add_param(¶m, "REQUEST_METHOD", 266 server_httpmethod_byid(desc->http_method), clt) == -1) { 267 errstr = "failed to encode param"; 268 goto fail; 269 } 270 271 if (!desc->http_query) { 272 if (fcgi_add_param(¶m, "REQUEST_URI", desc->http_path, 273 clt) == -1) { 274 errstr = "failed to encode param"; 275 goto fail; 276 } 277 } else if (asprintf(&str, "%s?%s", desc->http_path, 278 desc->http_query) != -1) { 279 ret = fcgi_add_param(¶m, "REQUEST_URI", str, clt); 280 free(str); 281 if (ret == -1) { 282 errstr = "failed to encode param"; 283 goto fail; 284 } 285 } 286 287 (void)print_host(&clt->clt_srv_ss, hbuf, sizeof(hbuf)); 288 if (fcgi_add_param(¶m, "SERVER_ADDR", hbuf, clt) == -1) { 289 errstr = "failed to encode param"; 290 goto fail; 291 } 292 293 (void)snprintf(hbuf, sizeof(hbuf), "%d", 294 ntohs(server_socket_getport(&clt->clt_srv_ss))); 295 if (fcgi_add_param(¶m, "SERVER_PORT", hbuf, clt) == -1) { 296 errstr = "failed to encode param"; 297 goto fail; 298 } 299 300 if (fcgi_add_param(¶m, "SERVER_NAME", srv_conf->name, 301 clt) == -1) { 302 errstr = "failed to encode param"; 303 goto fail; 304 } 305 306 if (fcgi_add_param(¶m, "SERVER_PROTOCOL", desc->http_version, 307 clt) == -1) { 308 errstr = "failed to encode param"; 309 goto fail; 310 } 311 312 if (fcgi_add_param(¶m, "SERVER_SOFTWARE", HTTPD_SERVERNAME, 313 clt) == -1) { 314 errstr = "failed to encode param"; 315 goto fail; 316 } 317 318 if (param.total_len != 0) { /* send last params record */ 319 bufferevent_write(clt->clt_srvbev, ¶m.buf, 320 sizeof(struct fcgi_record_header) + 321 ntohs(h->content_len)); 322 } 323 324 /* send "no more params" message */ 325 h->content_len = 0; 326 bufferevent_write(clt->clt_srvbev, ¶m.buf, 327 sizeof(struct fcgi_record_header)); 328 329 h->type = FCGI_STDIN; 330 331 bufferevent_write(clt->clt_srvbev, ¶m.buf, 332 sizeof(struct fcgi_record_header)); 333 334 bufferevent_settimeout(clt->clt_srvbev, 335 srv_conf->timeout.tv_sec, srv_conf->timeout.tv_sec); 336 bufferevent_enable(clt->clt_srvbev, EV_READ|EV_WRITE); 337 bufferevent_disable(clt->clt_bev, EV_READ); 338 339 /* 340 * persist is not supported yet because we don't get the 341 * Content-Length from slowcgi and don't support chunked encoding. 342 */ 343 clt->clt_persist = 0; 344 clt->clt_done = 0; 345 346 free(script); 347 return (0); 348 fail: 349 free(script); 350 if (errstr == NULL) 351 errstr = strerror(errno); 352 server_abort_http(clt, 500, errstr); 353 return (-1); 354} 355 356int 357fcgi_add_param(struct server_fcgi_param *p, const char *key, 358 const char *val, struct client *clt) 359{ 360 struct fcgi_record_header *h; 361 int len = 0; 362 int key_len = strlen(key); 363 int val_len = strlen(val); 364 uint8_t *param; 365 366 len += key_len + val_len; 367 len += key_len > 127 ? 4 : 1; 368 len += val_len > 127 ? 4 : 1; 369 370 DPRINTF("%s: %s[%d] => %s[%d], total_len: %d", __func__, key, key_len, 371 val, val_len, p->total_len); 372 373 if (len > FCGI_CONTENT_SIZE) 374 return (-1); 375 376 if (p->total_len + len > FCGI_CONTENT_SIZE) { 377 bufferevent_write(clt->clt_srvbev, p->buf, 378 sizeof(struct fcgi_record_header) + p->total_len); 379 p->total_len = 0; 380 } 381 382 h = (struct fcgi_record_header *)p->buf; 383 param = p->buf + sizeof(*h) + p->total_len; 384 385 if (key_len > 127) { 386 *param++ = ((key_len >> 24) & 0xff) | 0x80; 387 *param++ = ((key_len >> 16) & 0xff); 388 *param++ = ((key_len >> 8) & 0xff); 389 *param++ = (key_len & 0xff); 390 } else 391 *param++ = key_len; 392 393 if (val_len > 127) { 394 *param++ = ((val_len >> 24) & 0xff) | 0x80; 395 *param++ = ((val_len >> 16) & 0xff); 396 *param++ = ((val_len >> 8) & 0xff); 397 *param++ = (val_len & 0xff); 398 } else 399 *param++ = val_len; 400 401 memcpy(param, key, key_len); 402 param += key_len; 403 memcpy(param, val, val_len); 404 405 p->total_len += len; 406 407 h->content_len = htons(p->total_len); 408 return (0); 409} 410 411void 412server_fcgi_read(struct bufferevent *bev, void *arg) 413{ 414 uint8_t buf[FCGI_RECORD_SIZE]; 415 struct client *clt = (struct client *) arg; 416 struct fcgi_record_header *h; 417 size_t len; 418 419 do { 420 len = bufferevent_read(bev, &buf, clt->clt_fcgi_toread); 421 /* XXX error handling */ 422 evbuffer_add(clt->clt_srvevb, &buf, len); 423 clt->clt_fcgi_toread -= len; 424 DPRINTF("%s: len: %lu toread: %d state: %d", __func__, len, 425 clt->clt_fcgi_toread, clt->clt_fcgi_state); 426 427 if (clt->clt_fcgi_toread != 0) 428 return; 429 430 switch (clt->clt_fcgi_state) { 431 case FCGI_READ_HEADER: 432 clt->clt_fcgi_state = FCGI_READ_CONTENT; 433 h = (struct fcgi_record_header *) 434 EVBUFFER_DATA(clt->clt_srvevb); 435 DPRINTF("%s: record header: version %d type %d id %d " 436 "content len %d padding %d", __func__, 437 h->version, h->type, ntohs(h->id), 438 ntohs(h->content_len), h->padding_len); 439 clt->clt_fcgi_type = h->type; 440 clt->clt_fcgi_toread = ntohs(h->content_len); 441 clt->clt_fcgi_padding_len = h->padding_len; 442 evbuffer_drain(clt->clt_srvevb, 443 EVBUFFER_LENGTH(clt->clt_srvevb)); 444 if (clt->clt_fcgi_toread != 0) 445 break; 446 447 /* fallthrough if content_len == 0 */ 448 case FCGI_READ_CONTENT: 449 if (clt->clt_fcgi_type == FCGI_STDOUT && 450 EVBUFFER_LENGTH(clt->clt_srvevb) > 0) { 451 if (++clt->clt_chunk == 1) 452 server_fcgi_header(clt, 200); 453 server_bufferevent_write_buffer(clt, 454 clt->clt_srvevb); 455 } 456 evbuffer_drain(clt->clt_srvevb, 457 EVBUFFER_LENGTH(clt->clt_srvevb)); 458 if (!clt->clt_fcgi_padding_len) { 459 clt->clt_fcgi_state = FCGI_READ_HEADER; 460 clt->clt_fcgi_toread = 461 sizeof(struct fcgi_record_header); 462 } else { 463 clt->clt_fcgi_state = FCGI_READ_PADDING; 464 clt->clt_fcgi_toread = 465 clt->clt_fcgi_padding_len; 466 } 467 break; 468 case FCGI_READ_PADDING: 469 evbuffer_drain(clt->clt_srvevb, 470 EVBUFFER_LENGTH(clt->clt_srvevb)); 471 clt->clt_fcgi_state = FCGI_READ_HEADER; 472 clt->clt_fcgi_toread = 473 sizeof(struct fcgi_record_header); 474 break; 475 } 476 } while (len > 0); 477} 478 479int 480server_fcgi_header(struct client *clt, u_int code) 481{ 482 struct http_descriptor *desc = clt->clt_desc; 483 const char *error; 484 char tmbuf[32]; 485 486 if (desc == NULL || (error = server_httperror_byid(code)) == NULL) 487 return (-1); 488 489 if (server_log_http(clt, code, 0) == -1) 490 return (-1); 491 492 kv_purge(&desc->http_headers); 493 494 /* Add error codes */ 495 if (kv_setkey(&desc->http_pathquery, "%lu", code) == -1 || 496 kv_set(&desc->http_pathquery, "%s", error) == -1) 497 return (-1); 498 499 /* Add headers */ 500 if (kv_add(&desc->http_headers, "Server", HTTPD_SERVERNAME) == NULL) 501 return (-1); 502 503 /* Is it a persistent connection? */ 504 if (clt->clt_persist) { 505 if (kv_add(&desc->http_headers, 506 "Connection", "keep-alive") == NULL) 507 return (-1); 508 } else if (kv_add(&desc->http_headers, "Connection", "close") == NULL) 509 return (-1); 510 511 /* Date header is mandatory and should be added last */ 512 server_http_date(tmbuf, sizeof(tmbuf)); 513 if (kv_add(&desc->http_headers, "Date", tmbuf) == NULL) 514 return (-1); 515 516 /* Write initial header (fcgi might append more) */ 517 if (server_writeresponse_http(clt) == -1 || 518 server_bufferevent_print(clt, "\r\n") == -1 || 519 server_headers(clt, server_writeheader_http, NULL) == -1) 520 return (-1); 521 522 return (0); 523} 524 525int 526server_fcgi_writeheader(struct client *clt, struct kv *hdr, void *arg) 527{ 528 struct server_fcgi_param *param = arg; 529 char *val, *name, *p; 530 const char *key; 531 int ret; 532 533 if (hdr->kv_flags & KV_FLAG_INVALID) 534 return (0); 535 536 /* The key might have been updated in the parent */ 537 if (hdr->kv_parent != NULL && hdr->kv_parent->kv_key != NULL) 538 key = hdr->kv_parent->kv_key; 539 else 540 key = hdr->kv_key; 541 542 val = hdr->kv_value; 543 544 if (strcasecmp(key, "Content-Length") == 0 || 545 strcasecmp(key, "Content-Type") == 0) { 546 if ((name = strdup(key)) == NULL) 547 return (-1); 548 } else { 549 if (asprintf(&name, "HTTP_%s", key) == -1) 550 return (-1); 551 } 552 553 for (p = name; *p != '\0'; p++) { 554 if (isalpha((unsigned char)*p)) 555 *p = toupper((unsigned char)*p); 556 else 557 *p = '_'; 558 } 559 560 ret = fcgi_add_param(param, name, val, clt); 561 free(name); 562 563 return (ret); 564} 565