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(&param, 0, sizeof(param));
173
174	h = (struct fcgi_record_header *)&param.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 *)&param.buf[sizeof(struct
182	    fcgi_record_header)];
183	begin->role = htons(FCGI_RESPONDER);
184
185	bufferevent_write(clt->clt_srvbev, &param.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(&param, "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(&param, "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(&param, "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(&param, "QUERY_STRING", desc->http_query,
220		    clt) == -1) {
221			errstr = "failed to encode param";
222			goto fail;
223		}
224
225	if (fcgi_add_param(&param, "DOCUMENT_ROOT", srv_conf->root,
226	    clt) == -1) {
227		errstr = "failed to encode param";
228		goto fail;
229	}
230	if (fcgi_add_param(&param, "DOCUMENT_URI", desc->http_path,
231	    clt) == -1) {
232		errstr = "failed to encode param";
233		goto fail;
234	}
235	if (fcgi_add_param(&param, "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, &param) == -1) {
243		errstr = "failed to encode param";
244		goto fail;
245	}
246
247	if (srv_conf->flags & SRVFLAG_SSL)
248		if (fcgi_add_param(&param, "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(&param, "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(&param, "REMOTE_PORT", hbuf, clt) == -1) {
261		errstr = "failed to encode param";
262		goto fail;
263	}
264
265	if (fcgi_add_param(&param, "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(&param, "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(&param, "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(&param, "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(&param, "SERVER_PORT", hbuf, clt) == -1) {
296		errstr = "failed to encode param";
297		goto fail;
298	}
299
300	if (fcgi_add_param(&param, "SERVER_NAME", srv_conf->name,
301	    clt) == -1) {
302		errstr = "failed to encode param";
303		goto fail;
304	}
305
306	if (fcgi_add_param(&param, "SERVER_PROTOCOL", desc->http_version,
307	    clt) == -1) {
308		errstr = "failed to encode param";
309		goto fail;
310	}
311
312	if (fcgi_add_param(&param, "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, &param.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, &param.buf,
327	    sizeof(struct fcgi_record_header));
328
329	h->type = FCGI_STDIN;
330
331	bufferevent_write(clt->clt_srvbev, &param.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