xref: /lighttpd1.4/src/mod_simple_vhost.c (revision 49e9f0ac)
1 #include "first.h"
2 
3 #include "base.h"
4 #include "log.h"
5 #include "buffer.h"
6 #include "stat_cache.h"
7 
8 #include "plugin.h"
9 
10 #include <stdlib.h>
11 #include <string.h>
12 #include <errno.h>
13 
14 typedef struct {
15 	buffer *server_root;
16 	buffer *default_host;
17 	buffer *document_root;
18 
19 	buffer *docroot_cache_key;
20 	buffer *docroot_cache_value;
21 	buffer *docroot_cache_servername;
22 
23 	unsigned short debug;
24 } plugin_config;
25 
26 typedef struct {
27 	PLUGIN_DATA;
28 
29 	buffer *doc_root;
30 
31 	plugin_config **config_storage;
32 	plugin_config conf;
33 } plugin_data;
34 
35 INIT_FUNC(mod_simple_vhost_init) {
36 	plugin_data *p;
37 
38 	p = calloc(1, sizeof(*p));
39 
40 	p->doc_root = buffer_init();
41 
42 	return p;
43 }
44 
45 FREE_FUNC(mod_simple_vhost_free) {
46 	plugin_data *p = p_d;
47 
48 	UNUSED(srv);
49 
50 	if (!p) return HANDLER_GO_ON;
51 
52 	if (p->config_storage) {
53 		size_t i;
54 		for (i = 0; i < srv->config_context->used; i++) {
55 			plugin_config *s = p->config_storage[i];
56 			if (NULL == s) continue;
57 
58 			buffer_free(s->document_root);
59 			buffer_free(s->default_host);
60 			buffer_free(s->server_root);
61 
62 			buffer_free(s->docroot_cache_key);
63 			buffer_free(s->docroot_cache_value);
64 			buffer_free(s->docroot_cache_servername);
65 
66 			free(s);
67 		}
68 
69 		free(p->config_storage);
70 	}
71 
72 	buffer_free(p->doc_root);
73 
74 	free(p);
75 
76 	return HANDLER_GO_ON;
77 }
78 
79 SETDEFAULTS_FUNC(mod_simple_vhost_set_defaults) {
80 	plugin_data *p = p_d;
81 	size_t i;
82 
83 	config_values_t cv[] = {
84 		{ "simple-vhost.server-root",       NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
85 		{ "simple-vhost.default-host",      NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
86 		{ "simple-vhost.document-root",     NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
87 		{ "simple-vhost.debug",             NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },
88 		{ NULL,                             NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
89 	};
90 
91 	if (!p) return HANDLER_ERROR;
92 
93 	p->config_storage = calloc(srv->config_context->used, sizeof(plugin_config *));
94 
95 	for (i = 0; i < srv->config_context->used; i++) {
96 		data_config const* config = (data_config const*)srv->config_context->data[i];
97 		plugin_config *s;
98 
99 		s = calloc(1, sizeof(plugin_config));
100 
101 		s->server_root = buffer_init();
102 		s->default_host = buffer_init();
103 		s->document_root = buffer_init();
104 
105 		s->docroot_cache_key = buffer_init();
106 		s->docroot_cache_value = buffer_init();
107 		s->docroot_cache_servername = buffer_init();
108 
109 		s->debug = 0;
110 
111 		cv[0].destination = s->server_root;
112 		cv[1].destination = s->default_host;
113 		cv[2].destination = s->document_root;
114 		cv[3].destination = &(s->debug);
115 
116 
117 		p->config_storage[i] = s;
118 
119 		if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
120 			return HANDLER_ERROR;
121 		}
122 
123 		if (!buffer_string_is_empty(s->server_root))
124 			buffer_append_slash(s->server_root);
125 		if (!buffer_string_is_empty(s->document_root))
126 			buffer_append_slash(s->document_root);
127 	}
128 
129 	return HANDLER_GO_ON;
130 }
131 
132 static void build_doc_root_path(buffer *out, buffer *sroot, buffer *host, buffer *droot) {
133 	force_assert(!buffer_string_is_empty(sroot));
134 	buffer_copy_buffer(out, sroot);
135 
136 	if (!buffer_string_is_empty(host)) {
137 		/* a hostname has to start with a alpha-numerical character
138 		 * and must not contain a slash "/"
139 		 */
140 		char *dp;
141 		if (NULL == (dp = strchr(host->ptr, ':'))) {
142 			buffer_append_string_buffer(out, host);
143 		} else {
144 			buffer_append_string_len(out, host->ptr, dp - host->ptr);
145 		}
146 	}
147 
148 	if (!buffer_string_is_empty(droot)) {
149 		buffer_append_path_len(out, CONST_BUF_LEN(droot));
150 	}
151 	else {
152 		buffer_append_slash(out);
153 	}
154 }
155 
156 static int build_doc_root(server *srv, connection *con, plugin_data *p, buffer *out, buffer *host) {
157 	stat_cache_entry *sce = NULL;
158 
159 	build_doc_root_path(out, p->conf.server_root, host, p->conf.document_root);
160 
161 	if (HANDLER_ERROR == stat_cache_get_entry(srv, con, out, &sce)) {
162 		if (p->conf.debug) {
163 			log_error_write(srv, __FILE__, __LINE__, "sb",
164 					strerror(errno), out);
165 		}
166 		return -1;
167 	} else if (!S_ISDIR(sce->st.st_mode)) {
168 		return -1;
169 	}
170 
171 	return 0;
172 }
173 
174 
175 #define PATCH(x) \
176 	p->conf.x = s->x;
177 static int mod_simple_vhost_patch_connection(server *srv, connection *con, plugin_data *p) {
178 	size_t i, j;
179 	plugin_config *s = p->config_storage[0];
180 
181 	PATCH(server_root);
182 	PATCH(default_host);
183 	PATCH(document_root);
184 
185 	PATCH(docroot_cache_key);
186 	PATCH(docroot_cache_value);
187 	PATCH(docroot_cache_servername);
188 
189 	PATCH(debug);
190 
191 	/* skip the first, the global context */
192 	for (i = 1; i < srv->config_context->used; i++) {
193 		data_config *dc = (data_config *)srv->config_context->data[i];
194 		s = p->config_storage[i];
195 
196 		/* condition didn't match */
197 		if (!config_check_cond(srv, con, dc)) continue;
198 
199 		/* merge config */
200 		for (j = 0; j < dc->value->used; j++) {
201 			data_unset *du = dc->value->data[j];
202 
203 			if (buffer_is_equal_string(du->key, CONST_STR_LEN("simple-vhost.server-root"))) {
204 				PATCH(server_root);
205 				PATCH(docroot_cache_key);
206 				PATCH(docroot_cache_value);
207 				PATCH(docroot_cache_servername);
208 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("simple-vhost.default-host"))) {
209 				PATCH(default_host);
210 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("simple-vhost.document-root"))) {
211 				PATCH(document_root);
212 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("simple-vhost.debug"))) {
213 				PATCH(debug);
214 			}
215 		}
216 	}
217 
218 	return 0;
219 }
220 #undef PATCH
221 
222 static handler_t mod_simple_vhost_docroot(server *srv, connection *con, void *p_data) {
223 	plugin_data *p = p_data;
224 
225 	/*
226 	 * cache the last successfull translation from hostname (authority) to docroot
227 	 * - this saves us a stat() call
228 	 *
229 	 */
230 
231 	mod_simple_vhost_patch_connection(srv, con, p);
232 
233 	/* build_doc_root() requires a server_root; skip module if simple-vhost.server-root is not set
234 	 * or set to an empty string (especially don't cache any results!)
235 	 */
236 	if (buffer_string_is_empty(p->conf.server_root)) return HANDLER_GO_ON;
237 
238 	if (!buffer_string_is_empty(p->conf.docroot_cache_key) &&
239 	    !buffer_string_is_empty(con->uri.authority) &&
240 	    buffer_is_equal(p->conf.docroot_cache_key, con->uri.authority)) {
241 		/* cache hit */
242 		buffer_copy_buffer(con->server_name,       p->conf.docroot_cache_servername);
243 		buffer_copy_buffer(con->physical.doc_root, p->conf.docroot_cache_value);
244 	} else {
245 		/* build document-root */
246 		if (buffer_string_is_empty(con->uri.authority) ||
247 		    build_doc_root(srv, con, p, p->doc_root, con->uri.authority)) {
248 			/* not found, fallback the default-host */
249 			if (0 == build_doc_root(srv, con, p,
250 					   p->doc_root,
251 					   p->conf.default_host)) {
252 				/* default host worked */
253 				buffer_copy_buffer(con->server_name, p->conf.default_host);
254 				buffer_copy_buffer(con->physical.doc_root, p->doc_root);
255 				/* do not cache default host */
256 			}
257 			return HANDLER_GO_ON;
258 		}
259 
260 		/* found host */
261 		buffer_copy_buffer(con->server_name, con->uri.authority);
262 		buffer_copy_buffer(con->physical.doc_root, p->doc_root);
263 
264 		/* copy to cache */
265 		buffer_copy_buffer(p->conf.docroot_cache_key,        con->uri.authority);
266 		buffer_copy_buffer(p->conf.docroot_cache_value,      p->doc_root);
267 		buffer_copy_buffer(p->conf.docroot_cache_servername, con->server_name);
268 	}
269 
270 	return HANDLER_GO_ON;
271 }
272 
273 
274 int mod_simple_vhost_plugin_init(plugin *p);
275 int mod_simple_vhost_plugin_init(plugin *p) {
276 	p->version     = LIGHTTPD_VERSION_ID;
277 	p->name        = buffer_init_string("simple_vhost");
278 
279 	p->init        = mod_simple_vhost_init;
280 	p->set_defaults = mod_simple_vhost_set_defaults;
281 	p->handle_docroot  = mod_simple_vhost_docroot;
282 	p->cleanup     = mod_simple_vhost_free;
283 
284 	p->data        = NULL;
285 
286 	return 0;
287 }
288