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