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