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