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