server_fcgi.c revision 1.3
1/*	$OpenBSD: server_fcgi.c,v 1.3 2014/07/31 14:25:14 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_srvbev != NULL)
120		bufferevent_free(clt->clt_srvbev);
121	clt->clt_srvbev = bufferevent_new(fd, server_fcgi_read,
122	    NULL, server_fcgi_error, clt);
123	if (clt->clt_srvbev == NULL) {
124		errstr = "failed to allocate fcgi buffer event";
125		goto fail;
126	}
127	bufferevent_settimeout(clt->clt_srvbev,
128	    srv_conf->timeout.tv_sec, srv_conf->timeout.tv_sec);
129	bufferevent_enable(clt->clt_srvbev, 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_srvbev, &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_srvbev, &buf,
162	    sizeof(struct fcgi_record_header) +
163	    ntohs(h->content_len));
164
165	h->content_len = 0;
166
167	bufferevent_write(clt->clt_srvbev, &buf,
168	    sizeof(struct fcgi_record_header));
169
170	h->type = FCGI_STDIN;
171
172	bufferevent_write(clt->clt_srvbev, &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