xref: /openbsd-src/usr.sbin/httpd/server_file.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: server_file.c,v 1.6 2014/07/16 10:25:28 reyk Exp $	*/
2 
3 /*
4  * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@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 void	 server_file_error(struct bufferevent *, short, void *);
50 
51 int
52 server_file(struct httpd *env, struct client *clt)
53 {
54 	struct http_descriptor	*desc = clt->clt_desc;
55 	struct server		*srv = clt->clt_server;
56 	struct media_type	*media;
57 	const char		*errstr = NULL;
58 	int			 fd = -1, ret;
59 	char			 path[MAXPATHLEN];
60 	struct stat		 st;
61 
62 	/*
63 	 * XXX This is not ready XXX
64 	 * XXX Don't expect anything from this code yet,
65 	 */
66 
67 	strlcpy(path, "/htdocs", sizeof(path));
68 	if (desc->http_path[0] != '/')
69 		strlcat(path, "/", sizeof(path));
70 	strlcat(path, desc->http_path, sizeof(path));
71 	if (desc->http_path[strlen(desc->http_path) - 1] == '/')
72 		strlcat(path, "index.html", sizeof(path));
73 
74 	if (access(path, R_OK) == -1) {
75 		strlcpy(path, desc->http_path, sizeof(path));
76 		switch (errno) {
77 		case EACCES:
78 			server_abort_http(clt, 403, path);
79 			break;
80 		case ENOENT:
81 			server_abort_http(clt, 404, path);
82 			break;
83 		default:
84 			server_abort_http(clt, 500, path);
85 			break;
86 		}
87 		return (-1);
88 	}
89 
90 	if ((fd = open(path, O_RDONLY)) == -1 || fstat(fd, &st) == -1)
91 		goto fail;
92 
93 	/* File descriptor is opened, decrement inflight counter */
94 	server_inflight_dec(clt, __func__);
95 
96 	media = media_find(env->sc_mediatypes, path);
97 	ret = server_response_http(clt, 200, media, st.st_size);
98 	switch (ret) {
99 	case -1:
100 		goto fail;
101 	case 0:
102 		/* Connection is already finished */
103 		close(fd);
104 		return (0);
105 	default:
106 		break;
107 	}
108 
109 	clt->clt_fd = fd;
110 	if (clt->clt_file != NULL)
111 		bufferevent_free(clt->clt_file);
112 
113 	clt->clt_file = bufferevent_new(clt->clt_fd, server_read,
114 	    server_write, server_file_error, clt);
115 	if (clt->clt_file == NULL) {
116 		errstr = "failed to allocate file buffer event";
117 		goto fail;
118 	}
119 
120 	bufferevent_settimeout(clt->clt_file,
121 	    srv->srv_conf.timeout.tv_sec, srv->srv_conf.timeout.tv_sec);
122 	bufferevent_enable(clt->clt_file, EV_READ);
123 	bufferevent_disable(clt->clt_bev, EV_READ);
124 
125 	return (0);
126  fail:
127 	if (errstr == NULL)
128 		errstr = strerror(errno);
129 	server_abort_http(clt, 500, errstr);
130 	return (-1);
131 }
132 
133 void
134 server_file_error(struct bufferevent *bev, short error, void *arg)
135 {
136 	struct client		*clt = arg;
137 	struct evbuffer		*dst;
138 
139 	if (error & EVBUFFER_TIMEOUT) {
140 		server_close(clt, "buffer event timeout");
141 		return;
142 	}
143 	if (error & (EVBUFFER_READ|EVBUFFER_WRITE|EVBUFFER_EOF)) {
144 		bufferevent_disable(bev, EV_READ);
145 
146 		clt->clt_done = 1;
147 
148 		dst = EVBUFFER_OUTPUT(clt->clt_bev);
149 		if (EVBUFFER_LENGTH(dst)) {
150 			/* Finish writing all data first */
151 			bufferevent_enable(clt->clt_bev, EV_WRITE);
152 			return;
153 		}
154 
155 		if (clt->clt_persist) {
156 			/* Close input file and wait for next HTTP request */
157 			if (clt->clt_fd != -1)
158 				close(clt->clt_fd);
159 			clt->clt_fd = -1;
160 			clt->clt_toread = TOREAD_HTTP_HEADER;
161 			server_reset_http(clt);
162 			bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE);
163 			return;
164 		}
165 		server_close(clt, "done");
166 		return;
167 	}
168 	if (error & EVBUFFER_ERROR && errno == EFBIG) {
169 		bufferevent_enable(bev, EV_READ);
170 		return;
171 	}
172 	server_close(clt, "buffer event error");
173 	return;
174 }
175