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