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