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