xref: /lighttpd1.4/src/mod_vhostdb.c (revision 49e9f0ac)
1 #include "first.h"
2 
3 #include "base.h"
4 #include "plugin.h"
5 #include "http_vhostdb.h"
6 #include "log.h"
7 #include "stat_cache.h"
8 
9 #include <errno.h>
10 #include <stdlib.h>
11 #include <string.h>
12 
13 /**
14  * vhostdb framework
15  */
16 
17 typedef struct {
18     buffer *vhostdb_backend_conf;
19 
20     /* generated */
21     const http_vhostdb_backend_t *vhostdb_backend;
22 } plugin_config;
23 
24 typedef struct {
25     PLUGIN_DATA;
26     plugin_config **config_storage;
27     plugin_config conf;
28 
29     buffer *tmp_buf;
30 } plugin_data;
31 
32 INIT_FUNC(mod_vhostdb_init) {
33     plugin_data *p = calloc(1, sizeof(*p));
34     p->tmp_buf = buffer_init();
35     return p;
36 }
37 
38 FREE_FUNC(mod_vhostdb_free) {
39     plugin_data *p = p_d;
40     if (!p) return HANDLER_GO_ON;
41 
42     if (p->config_storage) {
43         size_t i;
44         for (i = 0; i < srv->config_context->used; i++) {
45             plugin_config *s = p->config_storage[i];
46             if (NULL == s) continue;
47             buffer_free(s->vhostdb_backend_conf);
48             free(s);
49         }
50         free(p->config_storage);
51     }
52 
53     free(p->tmp_buf);
54     free(p);
55 
56     UNUSED(srv);
57     return HANDLER_GO_ON;
58 }
59 
60 SETDEFAULTS_FUNC(mod_vhostdb_set_defaults) {
61     plugin_data *p = p_d;
62     config_values_t cv[] = {
63         { "vhostdb.backend",                NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
64 
65         { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
66     };
67 
68     p->config_storage = calloc(srv->config_context->used, sizeof(plugin_config *));
69 
70     for (size_t i = 0; i < srv->config_context->used; ++i) {
71         data_config const *config = (data_config const*)srv->config_context->data[i];
72         plugin_config *s = calloc(1, sizeof(plugin_config));
73         s->vhostdb_backend_conf = buffer_init();
74 
75         cv[0].destination = s->vhostdb_backend_conf;
76 
77         p->config_storage[i] = s;
78 
79         if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
80             return HANDLER_ERROR;
81         }
82 
83         if (!buffer_string_is_empty(s->vhostdb_backend_conf)) {
84             s->vhostdb_backend =
85               http_vhostdb_backend_get(s->vhostdb_backend_conf);
86             if (NULL == s->vhostdb_backend) {
87                 log_error_write(srv, __FILE__, __LINE__, "sb",
88                                 "vhostdb.backend not supported:",
89                                 s->vhostdb_backend_conf);
90                 return HANDLER_ERROR;
91             }
92         }
93     }
94 
95     return HANDLER_GO_ON;
96 }
97 
98 #define PATCH(x) \
99     p->conf.x = s->x;
100 static int mod_vhostdb_patch_connection(server *srv, connection *con, plugin_data *p) {
101     plugin_config *s = p->config_storage[0];
102     PATCH(vhostdb_backend);
103 
104     /* skip the first, the global context */
105     for (size_t i = 1; i < srv->config_context->used; ++i) {
106         data_config *dc = (data_config *)srv->config_context->data[i];
107         s = p->config_storage[i];
108 
109         /* condition didn't match */
110         if (!config_check_cond(srv, con, dc)) continue;
111 
112         /* merge config */
113         for (size_t j = 0; j < dc->value->used; ++j) {
114             data_unset *du = dc->value->data[j];
115 
116             if (buffer_is_equal_string(du->key, CONST_STR_LEN("vhostdb.backend"))) {
117                 PATCH(vhostdb_backend);
118             }
119         }
120     }
121 
122     return 0;
123 }
124 #undef PATCH
125 
126 typedef struct {
127     buffer *server_name;
128     buffer *document_root;
129 } vhostdb_entry;
130 
131 static vhostdb_entry * vhostdb_entry_init (void)
132 {
133     vhostdb_entry *ve = calloc(1, sizeof(*ve));
134     ve->server_name = buffer_init();
135     ve->document_root = buffer_init();
136     return ve;
137 }
138 
139 static void vhostdb_entry_free (vhostdb_entry *ve)
140 {
141     buffer_free(ve->server_name);
142     buffer_free(ve->document_root);
143     free(ve);
144 }
145 
146 CONNECTION_FUNC(mod_vhostdb_handle_connection_close) {
147     plugin_data *p = p_d;
148     vhostdb_entry *ve;
149 
150     if ((ve = con->plugin_ctx[p->id])) {
151         con->plugin_ctx[p->id] = NULL;
152         vhostdb_entry_free(ve);
153     }
154 
155     UNUSED(srv);
156     return HANDLER_GO_ON;
157 }
158 
159 static handler_t mod_vhostdb_error_500 (connection *con)
160 {
161     con->http_status = 500; /* Internal Server Error */
162     con->mode = DIRECT;
163     return HANDLER_FINISHED;
164 }
165 
166 static handler_t mod_vhostdb_found (connection *con, vhostdb_entry *ve)
167 {
168     /* fix virtual server and docroot */
169     buffer_copy_buffer(con->server_name, ve->server_name);
170     buffer_copy_buffer(con->physical.doc_root, ve->document_root);
171     return HANDLER_GO_ON;
172 }
173 
174 CONNECTION_FUNC(mod_vhostdb_handle_docroot) {
175     plugin_data *p = p_d;
176     vhostdb_entry *ve;
177     const http_vhostdb_backend_t *backend;
178     buffer *b;
179     stat_cache_entry *sce;
180 
181     /* no host specified? */
182     if (buffer_string_is_empty(con->uri.authority)) return HANDLER_GO_ON;
183 
184     /* XXX: future: implement larger, managed cache
185      * of database responses (positive and negative) */
186 
187     /* check if cached this connection */
188     ve = con->plugin_ctx[p->id];
189     if (ve && buffer_is_equal(ve->server_name, con->uri.authority)) {
190         return mod_vhostdb_found(con, ve); /* HANDLER_GO_ON */
191     }
192 
193     mod_vhostdb_patch_connection(srv, con, p);
194     if (!p->conf.vhostdb_backend) return HANDLER_GO_ON;
195 
196     b = p->tmp_buf;
197     backend = p->conf.vhostdb_backend;
198     if (0 != backend->query(srv, con, backend->p_d, b)) {
199         return mod_vhostdb_error_500(con); /* HANDLER_FINISHED */
200     }
201 
202     if (buffer_string_is_empty(b)) {
203         /* no such virtual host */
204         return HANDLER_GO_ON;
205     }
206 
207     /* sanity check that really is a directory */
208     buffer_append_slash(b);
209     if (HANDLER_ERROR == stat_cache_get_entry(srv, con, b, &sce)) {
210         log_error_write(srv, __FILE__, __LINE__, "sb", strerror(errno), b);
211         return mod_vhostdb_error_500(con); /* HANDLER_FINISHED */
212     }
213     if (!S_ISDIR(sce->st.st_mode)) {
214         log_error_write(srv, __FILE__, __LINE__, "sb", "Not a directory", b);
215         return mod_vhostdb_error_500(con); /* HANDLER_FINISHED */
216     }
217 
218     /* cache the data */
219     if (!ve) con->plugin_ctx[p->id] = ve = vhostdb_entry_init();
220     buffer_copy_buffer(ve->server_name, con->uri.authority);
221     buffer_copy_buffer(ve->document_root, b);
222 
223     return mod_vhostdb_found(con, ve); /* HANDLER_GO_ON */
224 }
225 
226 int mod_vhostdb_plugin_init(plugin *p);
227 int mod_vhostdb_plugin_init(plugin *p) {
228     p->version          = LIGHTTPD_VERSION_ID;
229     p->name             = buffer_init_string("vhostdb");
230     p->init             = mod_vhostdb_init;
231     p->cleanup          = mod_vhostdb_free;
232     p->set_defaults     = mod_vhostdb_set_defaults;
233     p->handle_docroot   = mod_vhostdb_handle_docroot;
234     p->connection_reset = mod_vhostdb_handle_connection_close;
235 
236     p->data             = NULL;
237 
238     return 0;
239 }
240