server_fcgi.c revision 1.2
1/* $OpenBSD: server_fcgi.c,v 1.2 2014/07/31 14:18:38 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 <err.h> 42#include <event.h> 43 44#include <openssl/ssl.h> 45 46#include "httpd.h" 47#include "http.h" 48 49#define FCGI_CONTENT_SIZE 65535 50#define FCGI_PADDING_SIZE 255 51#define FCGI_RECORD_SIZE \ 52 (sizeof(struct fcgi_record_header) + FCGI_CONTENT_SIZE + FCGI_PADDING_SIZE) 53 54#define FCGI_BEGIN_REQUEST 1 55#define FCGI_ABORT_REQUEST 2 56#define FCGI_END_REQUEST 3 57#define FCGI_PARAMS 4 58#define FCGI_STDIN 5 59#define FCGI_STDOUT 6 60#define FCGI_STDERR 7 61#define FCGI_DATA 8 62#define FCGI_GET_VALUES 9 63#define FCGI_GET_VALUES_RESULT 10 64#define FCGI_UNKNOWN_TYPE 11 65#define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE) 66 67#define FCGI_RESPONDER 1 68 69struct fcgi_record_header { 70 uint8_t version; 71 uint8_t type; 72 uint16_t id; 73 uint16_t content_len; 74 uint8_t padding_len; 75 uint8_t reserved; 76}__packed; 77 78struct fcgi_begin_request_body { 79 uint16_t role; 80 uint8_t flags; 81 uint8_t reserved[5]; 82}__packed; 83 84void server_fcgi_read(struct bufferevent *, void *); 85void server_fcgi_error(struct bufferevent *, short, void *); 86int fcgi_add_param(uint8_t *, char *, char *, int); 87 88int 89server_fcgi(struct httpd *env, struct client *clt) 90{ 91 struct server_config *srv_conf = clt->clt_srv_conf; 92 struct http_descriptor *desc = clt->clt_desc; 93 struct sockaddr_un sun; 94 struct fcgi_record_header *h; 95 struct fcgi_begin_request_body *begin; 96 size_t len, total_len; 97 int fd; 98 const char *errstr = NULL; 99 uint8_t buf[FCGI_RECORD_SIZE]; 100 uint8_t *params; 101 102 log_info("server_fcgi"); 103 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 104 goto fail; 105 106 bzero(&sun, sizeof(sun)); 107 sun.sun_family = AF_UNIX; 108 len = strlcpy(sun.sun_path, srv_conf->path, sizeof(sun.sun_path)); 109 if (len >= sizeof(sun.sun_path)) { 110 errstr = "socket path to long"; 111 goto fail; 112 } 113 sun.sun_len = len; 114 115 log_info("path: %s", sun.sun_path); 116 if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) 117 goto fail; 118 119 if (clt->clt_fcgi != NULL) 120 bufferevent_free(clt->clt_fcgi); 121 clt->clt_fcgi = bufferevent_new(fd, server_fcgi_read, 122 NULL, server_fcgi_error, clt); 123 if (clt->clt_fcgi == NULL) { 124 errstr = "failed to allocate fcgi buffer event"; 125 goto fail; 126 } 127 bufferevent_settimeout(clt->clt_fcgi, 128 srv_conf->timeout.tv_sec, srv_conf->timeout.tv_sec); 129 bufferevent_enable(clt->clt_fcgi, EV_READ); 130 131 bzero(&buf, sizeof(buf)); 132 133 h = (struct fcgi_record_header *) &buf; 134 h->version = 1; 135 h->type = FCGI_BEGIN_REQUEST; 136 h->id = htons(1); 137 h->content_len = htons(sizeof(struct fcgi_begin_request_body)); 138 h->padding_len = 0; 139 140 begin = (struct fcgi_begin_request_body *) &buf[sizeof(struct 141 fcgi_record_header)]; 142 begin->role = htons(FCGI_RESPONDER); 143 144 bufferevent_write(clt->clt_fcgi, &buf, 145 sizeof(struct fcgi_record_header) + 146 sizeof(struct fcgi_begin_request_body)); 147 148 h->type = FCGI_PARAMS; 149 h->content_len = 0; 150 params = &buf[sizeof(struct fcgi_record_header)]; 151 152 total_len = 0; 153 154 len = fcgi_add_param(params, "SCRIPT_NAME", desc->http_path, 155 FCGI_CONTENT_SIZE); 156 params += len; 157 total_len += len; 158 159 h->content_len = htons(total_len); 160 161 bufferevent_write(clt->clt_fcgi, &buf, 162 sizeof(struct fcgi_record_header) + 163 ntohs(h->content_len)); 164 165 h->content_len = 0; 166 167 bufferevent_write(clt->clt_fcgi, &buf, 168 sizeof(struct fcgi_record_header)); 169 170 h->type = FCGI_STDIN; 171 172 bufferevent_write(clt->clt_fcgi, &buf, 173 sizeof(struct fcgi_record_header)); 174 175 return (0); 176 fail: 177 if (errstr == NULL) 178 errstr = strerror(errno); 179 server_abort_http(clt, 500, errstr); 180 return (-1); 181} 182 183int 184fcgi_add_param(uint8_t *buf, char *key, char *val, int size) 185{ 186 int len = 0; 187 log_info("%s => %s", key, val); 188 buf[0] = strlen(key); 189 len++; 190 buf[1] = strlen(val); 191 len++; 192 len += strlcpy(buf + len, key, size - len); 193 len += strlcpy(buf + len, val, size - len); 194 195 return len; 196} 197 198void 199server_fcgi_read(struct bufferevent *bev, void *arg) 200{ 201 struct client *clt = (struct client *) arg; 202 struct fcgi_record_header *h; 203 uint8_t buf[FCGI_RECORD_SIZE]; 204 size_t len; 205 206 len = bufferevent_read(bev, &buf, FCGI_RECORD_SIZE); 207 208 log_info("server_fcgi_read: %lu", len); 209 210 h = (struct fcgi_record_header *) &buf; 211 log_info("h->version: %d", h->version); 212 log_info("h->type: %d", h->type); 213 log_info("h->id: %d", ntohs(h->id)); 214 log_info("h->content_len: %d", ntohs(h->content_len)); 215 216 if (h->type == FCGI_STDOUT && ntohs(h->content_len) > 0) { 217 log_info("%s", (char*) &buf + 218 sizeof(struct fcgi_record_header)); 219 server_bufferevent_print(clt, "HTTP/1.1 200 OK\r\n"); 220 server_bufferevent_print(clt, (char*) &buf + 221 sizeof(struct fcgi_record_header)); 222 223 } 224} 225 226void 227server_fcgi_error(struct bufferevent *bev, short error, void *arg) 228{ 229 log_info("server_fcgi_error: %d", error); 230} 231