/* * mod_vd.c for Apache 2.0 * * Copyright 2003 by Anthony Howe. All rights reserved. * * * Please refer to LICENSE.TXT for the License & Disclaimer. An * online copy of the current license for the current version can * be found at http://www.snert.com/Software/mod_vd/ */ #ifndef MAX_LEVELS #define MAX_LEVELS 10 #endif /*********************************************************************** *** No configuration below this point. ***********************************************************************/ #define MODULE "mod_vd" #define AUTHOR "achowe@snert.com" #define VERSION "2.0" #include "httpd.h" #include "http_config.h" #define CORE_PRIVATE #include "http_core.h" #undef CORE_PRIVATE #include "http_log.h" #include "http_protocol.h" #include "http_request.h" #include "apr.h" #include "apr_lib.h" #include "apr_strings.h" #define APR_WANT_STRFUNC #include "apr_want.h" module AP_MODULE_DECLARE_DATA vd_module; /*********************************************************************** *** Macros ***********************************************************************/ #define UNSET -1 #define MERGE(p, c) (c == UNSET ? p : c) #define MERGE_PTR(p, c) ((void *) c == (void *) 0 ? p : c) /*********************************************************************** *** Constants ***********************************************************************/ /*********************************************************************** *** Global variables - should be read-only by child processes. ***********************************************************************/ typedef struct conf { unsigned int vdChopPrefix; unsigned int vdChopSuffix; long vdPathPrefixLen; char *vdPathPrefix; long vdPathSuffixLen; char *vdPathSuffix; server_rec *server; int enable; } t_conf; static server_rec *vdMainServer; /*********************************************************************** *** Support routines. ***********************************************************************/ static const char * setUnsignedInt(const char *arg, unsigned int *number) { char *stop; long value; if (arg == (char *) 0 || *arg == '\0') return "Integer unspecified"; value = strtol(arg, &stop, 10); if (*stop != '\0') return "Not a decimal integer"; if (value < 0) return "Not a positive integer"; if (UINT_MAX < value) return "Integer too large"; *number = (unsigned int) value; return (const char *) 0; } /*********************************************************************** *** Phase Handlers ***********************************************************************/ static int vdTranslateName(request_rec *r) { long dots; int i, j, n; t_conf *conf; apr_status_t rc; apr_finfo_t finfo; char *path, *next; const char *dot[MAX_LEVELS+1]; conf = ap_get_module_config(r->server->module_config, &vd_module); if (conf == (t_conf *) 0 || !conf->enable) return DECLINED; dot[0] = apr_table_get(r->headers_in, "Host"); if (dot[0] == (char *) 0) return DECLINED; /* Count dots in the request host name and the string length. */ for (dots = 1, next = (char *) dot[0]; *next != '\0' && *next != ':'; next++) { if (*next == '.') { if (MAX_LEVELS <= dots) return HTTP_INTERNAL_SERVER_ERROR; dot[dots++] = next; } } /* End of host name string. */ dot[dots] = next; /* At this point: * * dot[0] is the start of the host header string. * dot[1] points to the first dot. * ... * dot[dots] points to the terminating \0 or colon. */ /* You can't handle fewer dots than those that would be removed. */ if (dots <= conf->vdChopPrefix + conf->vdChopSuffix) return DECLINED; /* Build filename path string. */ path = apr_palloc( r->pool, conf->vdPathPrefixLen + (dot[dots] - dot[0]) + conf->vdPathSuffixLen + strlen(r->uri) + 1 ); /* Copy initial directory prefix into the path buffer. */ next = path; (void) strcpy(path, conf->vdPathPrefix); next += conf->vdPathPrefixLen; /* Point the start of the host string at an imaginary leading dot. */ dot[0]--; /* Append levels from right to left to the path. */ for (i = conf->vdChopPrefix, j = dots - conf->vdChopSuffix; i < j--; ) { /* Compute the length of the level name less the dot. */ n = dot[j+1] - dot[j] - 1; /* Append the level to the path plus a slash. */ strncpy(next, dot[j] + 1, n); next += n; *next++ = '/'; } /* Append the path suffix less the leading slash. */ (void) strcpy(next, conf->vdPathSuffix + 1); next += conf->vdPathSuffixLen - 1; /* Append requested URI. */ (void) strcpy(next, r->uri); ap_log_error( APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, vdMainServer, "host=[%s] chopLHS=%d chopRHS=%d prefix=[%s] suffix=[%s] path=[%s]", dot[0]+1, conf->vdChopPrefix, conf->vdChopSuffix, conf->vdPathPrefix, conf->vdPathSuffix, path ); /* Does the path exist? */ rc = apr_stat(&finfo, path, APR_FINFO_MIN, r->pool); if (rc != APR_SUCCESS || finfo.filetype == APR_NOFILE) return DECLINED; /* Set the final filename for the request. */ r->filename = path; r->finfo = finfo; return OK; } /*********************************************************************** *** Server configuration, merge, and initialisation. ***********************************************************************/ static void * vdCreateSrv(apr_pool_t *p, server_rec *s) { t_conf *conf; conf = (t_conf *) apr_palloc(p, sizeof *conf); conf->vdChopPrefix = 0; conf->vdChopSuffix = 0; conf->vdPathPrefix = (char *) 0; conf->vdPathPrefixLen = UNSET; conf->vdPathSuffix = ""; conf->vdPathSuffixLen = 0; conf->server = s; conf->enable = 1; return (void *) conf; } static void vdSetDefaultPrefix(apr_pool_t *p, t_conf *conf) { core_server_config *core; if (conf != (t_conf *) 0 && conf->vdPathPrefix == (char *) 0) { core = ap_get_module_config(conf->server->module_config, &core_module); conf->vdPathPrefix = (char *) core->ap_document_root; /* Append trailing slash to prefix. */ if (strrchr(conf->vdPathPrefix, '/')[1] != '\0') conf->vdPathPrefix = apr_pstrcat(p, conf->vdPathPrefix, "/", (char *) 0); conf->vdPathPrefixLen = strlen(conf->vdPathPrefix); } } static void * vdMergeSrv(apr_pool_t *p, void *basev, void *virtv) { vdSetDefaultPrefix(p, (t_conf *) basev); vdSetDefaultPrefix(p, (t_conf *) virtv); return virtv; } static int vdPostConfig(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) { t_conf *conf; core_server_config *core; vdMainServer = s; ap_add_version_component(pconf, MODULE "/" VERSION); conf = ap_get_module_config(vdMainServer->module_config, &vd_module); vdSetDefaultPrefix(pconf, conf); return OK; } static void vdHooks(apr_pool_t *p) { ap_hook_post_config(vdPostConfig, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_translate_name(vdTranslateName, NULL, NULL, APR_HOOK_MIDDLE); } /*********************************************************************** *** Configuration Directives ***********************************************************************/ /* * VdPathPrefix document-root-prefix * Context: global, */ static const char * VdPathPrefix(cmd_parms *cmd, void *dconf, const char *path) { t_conf *conf; apr_status_t rc; apr_finfo_t finfo; conf = ap_get_module_config(cmd->server->module_config, &vd_module); /* Append trailing slash to prefix. */ if (strrchr(path, '/')[1] != '\0') path = apr_pstrcat(cmd->temp_pool, path, "/", (char *) 0); conf->vdPathPrefix = ap_server_root_relative(cmd->pool, path); conf->vdPathPrefixLen = strlen(conf->vdPathPrefix); rc = apr_stat(&finfo, conf->vdPathPrefix, APR_FINFO_MIN, cmd->temp_pool); if (rc != APR_SUCCESS || finfo.filetype != APR_DIR) return "VdPathPrefix does not refer to directory."; return (const char *) 0; } /* * VdPathSuffix document-root-suffix * Context: global, */ static const char * VdPathSuffix(cmd_parms *cmd, void *dconf, const char *path) { t_conf *conf; conf = ap_get_module_config(cmd->server->module_config, &vd_module); /* Prepend slash to path suffix. */ if (path[0] != '/') path = apr_pstrcat(cmd->temp_pool, "/", path, (char *) 0); conf->vdPathSuffix = apr_pstrdup(cmd->pool, path); conf->vdPathSuffixLen = strlen(conf->vdPathSuffix); return (const char *) 0; } /* * VdChopSuffix number * Context: global, */ static const char * VdChopPrefix(cmd_parms *cmd, void *dconf, const char *number) { t_conf *conf; conf = ap_get_module_config(cmd->server->module_config, &vd_module); return setUnsignedInt(number, &conf->vdChopPrefix); } /* * VdChopSuffix number * Context: global, */ static const char * VdChopSuffix(cmd_parms *cmd, void *dconf, const char *number) { t_conf *conf; conf = ap_get_module_config(cmd->server->module_config, &vd_module); return setUnsignedInt(number, &conf->vdChopSuffix); } /* * VdEnable boolean * * context: global, */ static const char * VdEnable(cmd_parms *cmd, void *dconf, int flag) { t_conf *conf; conf = ap_get_module_config(cmd->server->module_config, &vd_module); conf->enable = flag; return (const char *) 0; } static command_rec vdCommands[] = { AP_INIT_FLAG( "VdEnable", VdEnable, NULL, RSRC_CONF, "Turn the module on or off." ), AP_INIT_TAKE1( "VdPathPrefix", VdPathPrefix, NULL, RSRC_CONF, "Virtual domain directory root prefix." ), AP_INIT_TAKE1( "VdPathSuffix", VdPathSuffix, NULL, RSRC_CONF, "Virtual domain directory root suffix." ), AP_INIT_TAKE1( "VdChopPrefix", VdChopPrefix, NULL, RSRC_CONF, "Number of domain levels to remove from the left hand side." ), AP_INIT_TAKE1( "VdChopSuffix", VdChopSuffix, NULL, RSRC_CONF, "Number of domain levels to remove from the right hand side." ), { NULL } }; /*********************************************************************** *** Module Definition Block ***********************************************************************/ /* Dispatch list for API hooks */ module AP_MODULE_DECLARE_DATA vd_module = { STANDARD20_MODULE_STUFF, NULL, /* create per-dir config structures */ NULL, /* merge per-dir config structures */ vdCreateSrv, /* create per-server config structures */ vdMergeSrv, /* merge per-server config structures */ vdCommands, /* apr_table_t of config file commands */ vdHooks /* register hooks */ };