xref: /lighttpd1.4/src/mod_vhostdb.c (revision 9570cc2f)
1 /*
2  * mod_vhostdb - virtual hosts mapping from backend database
3  *
4  * Copyright(c) 2017 Glenn Strauss gstrauss()gluelogic.com  All rights reserved
5  * License: BSD 3-clause (same as lighttpd)
6  */
7 #include "first.h"
8 
9 #include "base.h"
10 #include "plugin.h"
11 #include "http_vhostdb.h"
12 #include "log.h"
13 #include "stat_cache.h"
14 
15 #include <stdlib.h>
16 #include <string.h>
17 
18 /**
19  * vhostdb framework
20  */
21 
22 typedef struct {
23     const http_vhostdb_backend_t *vhostdb_backend;
24 } plugin_config;
25 
26 typedef struct {
27     PLUGIN_DATA;
28     plugin_config defaults;
29     plugin_config conf;
30 
31     buffer tmp_buf;
32 } plugin_data;
33 
34 INIT_FUNC(mod_vhostdb_init) {
35     return calloc(1, sizeof(plugin_data));
36 }
37 
38 FREE_FUNC(mod_vhostdb_free) {
39     plugin_data *p = p_d;
40     free(p->tmp_buf.ptr);
41 }
42 
43 static void mod_vhostdb_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) {
44     switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */
45       case 0: /* vhostdb.backend */
46         if (cpv->vtype == T_CONFIG_LOCAL)
47             pconf->vhostdb_backend = cpv->v.v;
48         break;
49       default:/* should not happen */
50         return;
51     }
52 }
53 
54 static void mod_vhostdb_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) {
55     do {
56         mod_vhostdb_merge_config_cpv(pconf, cpv);
57     } while ((++cpv)->k_id != -1);
58 }
59 
60 static void mod_vhostdb_patch_config(request_st * const r, plugin_data * const p) {
61     memcpy(&p->conf, &p->defaults, sizeof(plugin_config));
62     for (int i = 1, used = p->nconfig; i < used; ++i) {
63         if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id))
64             mod_vhostdb_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]);
65     }
66 }
67 
68 SETDEFAULTS_FUNC(mod_vhostdb_set_defaults) {
69     static const config_plugin_keys_t cpk[] = {
70       { CONST_STR_LEN("vhostdb.backend"),
71         T_CONFIG_STRING,
72         T_CONFIG_SCOPE_CONNECTION }
73      ,{ NULL, 0,
74         T_CONFIG_UNSET,
75         T_CONFIG_SCOPE_UNSET }
76     };
77 
78     plugin_data * const p = p_d;
79     if (!config_plugin_values_init(srv, p, cpk, "mod_vhostdb"))
80         return HANDLER_ERROR;
81 
82     /* process and validate config directives
83      * (init i to 0 if global context; to 1 to skip empty global context) */
84     for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
85         config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
86         for (; -1 != cpv->k_id; ++cpv) {
87             switch (cpv->k_id) {
88               case 0: /* vhostdb.backend */
89                 if (!buffer_string_is_empty(cpv->v.b)) {
90                     const buffer * const b = cpv->v.b;
91                     *(const void **)&cpv->v.v = http_vhostdb_backend_get(b);
92                     if (NULL == cpv->v.v) {
93                         log_error(srv->errh, __FILE__, __LINE__,
94                           "vhostdb.backend not supported: %s", b->ptr);
95                         return HANDLER_ERROR;
96                     }
97                     cpv->vtype = T_CONFIG_LOCAL;
98                 }
99                 break;
100               default:/* should not happen */
101                 break;
102             }
103         }
104     }
105 
106     /* initialize p->defaults from global config context */
107     if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
108         const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
109         if (-1 != cpv->k_id)
110             mod_vhostdb_merge_config(&p->defaults, cpv);
111     }
112 
113     return HANDLER_GO_ON;
114 }
115 
116 typedef struct {
117     buffer *server_name;
118     buffer *document_root;
119 } vhostdb_entry;
120 
121 static vhostdb_entry * vhostdb_entry_init (void)
122 {
123     vhostdb_entry *ve = calloc(1, sizeof(*ve));
124     ve->server_name = buffer_init();
125     ve->document_root = buffer_init();
126     return ve;
127 }
128 
129 static void vhostdb_entry_free (vhostdb_entry *ve)
130 {
131     buffer_free(ve->server_name);
132     buffer_free(ve->document_root);
133     free(ve);
134 }
135 
136 REQUEST_FUNC(mod_vhostdb_handle_connection_reset) {
137     plugin_data *p = p_d;
138     vhostdb_entry *ve;
139 
140     if ((ve = r->plugin_ctx[p->id])) {
141         r->plugin_ctx[p->id] = NULL;
142         vhostdb_entry_free(ve);
143     }
144 
145     return HANDLER_GO_ON;
146 }
147 
148 __attribute_cold__
149 static handler_t mod_vhostdb_error_500 (request_st * const r)
150 {
151     r->http_status = 500; /* Internal Server Error */
152     r->handler_module = NULL;
153     return HANDLER_FINISHED;
154 }
155 
156 static handler_t mod_vhostdb_found (request_st * const r, vhostdb_entry * const ve)
157 {
158     /* fix virtual server and docroot */
159     r->server_name = &r->server_name_buf;
160     buffer_copy_buffer(&r->server_name_buf, ve->server_name);
161     buffer_copy_buffer(&r->physical.doc_root, ve->document_root);
162     return HANDLER_GO_ON;
163 }
164 
165 REQUEST_FUNC(mod_vhostdb_handle_docroot) {
166     plugin_data *p = p_d;
167     vhostdb_entry *ve;
168     const http_vhostdb_backend_t *backend;
169     buffer *b;
170     stat_cache_entry *sce;
171 
172     /* no host specified? */
173     if (buffer_string_is_empty(&r->uri.authority)) return HANDLER_GO_ON;
174 
175     /* XXX: future: implement larger, managed cache
176      * of database responses (positive and negative) */
177 
178     /* check if cached this connection */
179     ve = r->plugin_ctx[p->id];
180     if (ve && buffer_is_equal(ve->server_name, &r->uri.authority)) {
181         return mod_vhostdb_found(r, ve); /* HANDLER_GO_ON */
182     }
183 
184     mod_vhostdb_patch_config(r, p);
185     if (!p->conf.vhostdb_backend) return HANDLER_GO_ON;
186 
187     b = &p->tmp_buf;
188     backend = p->conf.vhostdb_backend;
189     if (0 != backend->query(r, backend->p_d, b)) {
190         return mod_vhostdb_error_500(r); /* HANDLER_FINISHED */
191     }
192 
193     if (buffer_string_is_empty(b)) {
194         /* no such virtual host */
195         return HANDLER_GO_ON;
196     }
197 
198     /* sanity check that really is a directory */
199     buffer_append_slash(b);
200     sce = stat_cache_get_entry(b);
201     if (NULL == sce) {
202         log_perror(r->conf.errh, __FILE__, __LINE__, "%s", b->ptr);
203         return mod_vhostdb_error_500(r); /* HANDLER_FINISHED */
204     }
205     if (!S_ISDIR(sce->st.st_mode)) {
206         log_error(r->conf.errh, __FILE__, __LINE__,
207           "Not a directory: %s", b->ptr);
208         return mod_vhostdb_error_500(r); /* HANDLER_FINISHED */
209     }
210 
211     /* cache the data */
212     if (!ve) r->plugin_ctx[p->id] = ve = vhostdb_entry_init();
213     buffer_copy_buffer(ve->server_name, &r->uri.authority);
214     buffer_copy_buffer(ve->document_root, b);
215 
216     return mod_vhostdb_found(r, ve); /* HANDLER_GO_ON */
217 }
218 
219 int mod_vhostdb_plugin_init(plugin *p);
220 int mod_vhostdb_plugin_init(plugin *p) {
221     p->version          = LIGHTTPD_VERSION_ID;
222     p->name             = "vhostdb";
223     p->init             = mod_vhostdb_init;
224     p->cleanup          = mod_vhostdb_free;
225     p->set_defaults     = mod_vhostdb_set_defaults;
226     p->handle_docroot   = mod_vhostdb_handle_docroot;
227     p->connection_reset = mod_vhostdb_handle_connection_reset;
228 
229     return 0;
230 }
231