1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /*
18  * mod_vhost_alias.c: support for dynamically configured mass virtual hosting
19  *
20  * Copyright (c) 1998-1999 Demon Internet Ltd.
21  *
22  * This software was submitted by Demon Internet to the Apache Software Foundation
23  * in May 1999. Future revisions and derivatives of this source code
24  * must acknowledge Demon Internet as the original contributor of
25  * this module. All other licensing and usage conditions are those
26  * of the Apache Software Foundation.
27  *
28  * Originally written by Tony Finch <fanf@demon.net> <dot@dotat.at>.
29  *
30  * Implementation ideas were taken from mod_alias.c. The overall
31  * concept is derived from the OVERRIDE_DOC_ROOT/OVERRIDE_CGIDIR
32  * patch to Apache 1.3b3 and a similar feature in Demon's thttpd,
33  * both written by James Grinter <jrg@blodwen.demon.co.uk>.
34  */
35 
36 #include "apr.h"
37 #include "apr_strings.h"
38 #include "ap_hooks.h"
39 #include "apr_lib.h"
40 
41 #define APR_WANT_STRFUNC
42 #include "apr_want.h"
43 
44 #include "httpd.h"
45 #include "http_config.h"
46 #include "http_core.h"
47 #include "http_request.h"  /* for ap_hook_translate_name */
48 
49 
50 module AP_MODULE_DECLARE_DATA vhost_alias_module;
51 
52 
53 /*
54  * basic configuration things
55  * we abbreviate "mod_vhost_alias" to "mva" for shorter names
56  */
57 
58 typedef enum {
59     VHOST_ALIAS_UNSET, VHOST_ALIAS_NONE, VHOST_ALIAS_NAME, VHOST_ALIAS_IP
60 } mva_mode_e;
61 
62 /*
63  * Per-server module config record.
64  */
65 typedef struct mva_sconf_t {
66     const char *doc_root;
67     const char *cgi_root;
68     mva_mode_e doc_root_mode;
69     mva_mode_e cgi_root_mode;
70 } mva_sconf_t;
71 
mva_create_server_config(apr_pool_t * p,server_rec * s)72 static void *mva_create_server_config(apr_pool_t *p, server_rec *s)
73 {
74     mva_sconf_t *conf;
75 
76     conf = (mva_sconf_t *) apr_pcalloc(p, sizeof(mva_sconf_t));
77     conf->doc_root = NULL;
78     conf->cgi_root = NULL;
79     conf->doc_root_mode = VHOST_ALIAS_UNSET;
80     conf->cgi_root_mode = VHOST_ALIAS_UNSET;
81     return conf;
82 }
83 
mva_merge_server_config(apr_pool_t * p,void * parentv,void * childv)84 static void *mva_merge_server_config(apr_pool_t *p, void *parentv, void *childv)
85 {
86     mva_sconf_t *parent = (mva_sconf_t *) parentv;
87     mva_sconf_t *child = (mva_sconf_t *) childv;
88     mva_sconf_t *conf;
89 
90     conf = (mva_sconf_t *) apr_pcalloc(p, sizeof(*conf));
91     if (child->doc_root_mode == VHOST_ALIAS_UNSET) {
92         conf->doc_root_mode = parent->doc_root_mode;
93         conf->doc_root = parent->doc_root;
94     }
95     else {
96         conf->doc_root_mode = child->doc_root_mode;
97         conf->doc_root = child->doc_root;
98     }
99     if (child->cgi_root_mode == VHOST_ALIAS_UNSET) {
100         conf->cgi_root_mode = parent->cgi_root_mode;
101         conf->cgi_root = parent->cgi_root;
102     }
103     else {
104         conf->cgi_root_mode = child->cgi_root_mode;
105         conf->cgi_root = child->cgi_root;
106     }
107     return conf;
108 }
109 
110 
111 /*
112  * These are just here to tell us what vhost_alias_set should do.
113  * We don't put anything into them; we just use the cell addresses.
114  */
115 static int vhost_alias_set_doc_root_ip,
116     vhost_alias_set_cgi_root_ip,
117     vhost_alias_set_doc_root_name,
118     vhost_alias_set_cgi_root_name;
119 
vhost_alias_set(cmd_parms * cmd,void * dummy,const char * map)120 static const char *vhost_alias_set(cmd_parms *cmd, void *dummy, const char *map)
121 {
122     mva_sconf_t *conf;
123     mva_mode_e mode, *pmode;
124     const char **pmap;
125     const char *p;
126 
127     conf = (mva_sconf_t *) ap_get_module_config(cmd->server->module_config,
128                                                 &vhost_alias_module);
129     /* there ought to be a better way of doing this */
130     if (&vhost_alias_set_doc_root_ip == cmd->info) {
131         mode = VHOST_ALIAS_IP;
132         pmap = &conf->doc_root;
133         pmode = &conf->doc_root_mode;
134     }
135     else if (&vhost_alias_set_cgi_root_ip == cmd->info) {
136         mode = VHOST_ALIAS_IP;
137         pmap = &conf->cgi_root;
138         pmode = &conf->cgi_root_mode;
139     }
140     else if (&vhost_alias_set_doc_root_name == cmd->info) {
141         mode = VHOST_ALIAS_NAME;
142         pmap = &conf->doc_root;
143         pmode = &conf->doc_root_mode;
144     }
145     else if (&vhost_alias_set_cgi_root_name == cmd->info) {
146         mode = VHOST_ALIAS_NAME;
147         pmap = &conf->cgi_root;
148         pmode = &conf->cgi_root_mode;
149     }
150     else {
151         return "INTERNAL ERROR: unknown command info";
152     }
153 
154     if (!ap_os_is_path_absolute(cmd->pool, map)) {
155         if (ap_cstr_casecmp(map, "none")) {
156             return "format string must be an absolute path, or 'none'";
157         }
158         *pmap = NULL;
159         *pmode = VHOST_ALIAS_NONE;
160         return NULL;
161     }
162 
163     /* sanity check */
164     p = map;
165     while (*p != '\0') {
166         if (*p++ != '%') {
167             continue;
168         }
169         /* we just found a '%' */
170         if (*p == 'p' || *p == '%') {
171             ++p;
172             continue;
173         }
174         /* optional dash */
175         if (*p == '-') {
176             ++p;
177         }
178         /* digit N */
179         if (apr_isdigit(*p)) {
180             ++p;
181         }
182         else {
183             return "syntax error in format string";
184         }
185         /* optional plus */
186         if (*p == '+') {
187             ++p;
188         }
189         /* do we end here? */
190         if (*p != '.') {
191             continue;
192         }
193         ++p;
194         /* optional dash */
195         if (*p == '-') {
196             ++p;
197         }
198         /* digit M */
199         if (apr_isdigit(*p)) {
200             ++p;
201         }
202         else {
203             return "syntax error in format string";
204         }
205         /* optional plus */
206         if (*p == '+') {
207             ++p;
208         }
209     }
210     *pmap = map;
211     *pmode = mode;
212     return NULL;
213 }
214 
215 static const command_rec mva_commands[] =
216 {
217     AP_INIT_TAKE1("VirtualScriptAlias", vhost_alias_set,
218                   &vhost_alias_set_cgi_root_name, RSRC_CONF,
219                   "how to create a ScriptAlias based on the host"),
220     AP_INIT_TAKE1("VirtualDocumentRoot", vhost_alias_set,
221                   &vhost_alias_set_doc_root_name, RSRC_CONF,
222                   "how to create the DocumentRoot based on the host"),
223     AP_INIT_TAKE1("VirtualScriptAliasIP", vhost_alias_set,
224                   &vhost_alias_set_cgi_root_ip, RSRC_CONF,
225                   "how to create a ScriptAlias based on the host"),
226     AP_INIT_TAKE1("VirtualDocumentRootIP", vhost_alias_set,
227                   &vhost_alias_set_doc_root_ip, RSRC_CONF,
228                   "how to create the DocumentRoot based on the host"),
229     { NULL }
230 };
231 
232 
233 /*
234  * This really wants to be a nested function
235  * but C is too feeble to support them.
236  */
vhost_alias_checkspace(request_rec * r,char * buf,char ** pdest,int size)237 static APR_INLINE void vhost_alias_checkspace(request_rec *r, char *buf,
238                                              char **pdest, int size)
239 {
240     /* XXX: what if size > HUGE_STRING_LEN? */
241     if (*pdest + size > buf + HUGE_STRING_LEN) {
242         **pdest = '\0';
243         if (r->filename) {
244             r->filename = apr_pstrcat(r->pool, r->filename, buf, NULL);
245         }
246         else {
247             r->filename = apr_pstrdup(r->pool, buf);
248         }
249         *pdest = buf;
250     }
251 }
252 
vhost_alias_interpolate(request_rec * r,const char * name,const char * map,const char * uri)253 static void vhost_alias_interpolate(request_rec *r, const char *name,
254                                     const char *map, const char *uri)
255 {
256     /* 0..9 9..0 */
257     enum { MAXDOTS = 19 };
258     const char *dots[MAXDOTS+1];
259     int ndots;
260 
261     char buf[HUGE_STRING_LEN];
262     char *dest;
263     const char *docroot;
264 
265     int N, M, Np, Mp, Nd, Md;
266     const char *start, *end;
267 
268     const char *p;
269 
270     ndots = 0;
271     dots[ndots++] = name-1; /* slightly naughty */
272     for (p = name; *p; ++p) {
273         if (*p == '.' && ndots < MAXDOTS) {
274             dots[ndots++] = p;
275         }
276     }
277     dots[ndots] = p;
278 
279     r->filename = NULL;
280 
281     dest = buf;
282     while (*map) {
283         if (*map != '%') {
284             /* normal characters */
285             vhost_alias_checkspace(r, buf, &dest, 1);
286             *dest++ = *map++;
287             continue;
288         }
289         /* we are in a format specifier */
290         ++map;
291         /* %% -> % */
292         if (*map == '%') {
293             ++map;
294             vhost_alias_checkspace(r, buf, &dest, 1);
295             *dest++ = '%';
296             continue;
297         }
298         /* port number */
299         if (*map == 'p') {
300             ++map;
301             /* no. of decimal digits in a short plus one */
302             vhost_alias_checkspace(r, buf, &dest, 7);
303             dest += apr_snprintf(dest, 7, "%d", ap_get_server_port(r));
304             continue;
305         }
306         /* deal with %-N+.-M+ -- syntax is already checked */
307         M = 0;   /* value */
308         Np = Mp = 0; /* is there a plus? */
309         Nd = Md = 0; /* is there a dash? */
310         if (*map == '-') ++map, Nd = 1;
311         N = *map++ - '0';
312         if (*map == '+') ++map, Np = 1;
313         if (*map == '.') {
314             ++map;
315             if (*map == '-') {
316                 ++map, Md = 1;
317             }
318             M = *map++ - '0';
319             if (*map == '+') {
320                 ++map, Mp = 1;
321             }
322         }
323         /* note that N and M are one-based indices, not zero-based */
324         start = dots[0]+1; /* ptr to the first character */
325         end = dots[ndots]; /* ptr to the character after the last one */
326         if (N != 0) {
327             if (N > ndots) {
328                 start = "_";
329                 end = start+1;
330             }
331             else if (!Nd) {
332                 start = dots[N-1]+1;
333                 if (!Np) {
334                     end = dots[N];
335                 }
336             }
337             else {
338                 if (!Np) {
339                     start = dots[ndots-N]+1;
340                 }
341                 end = dots[ndots-N+1];
342             }
343         }
344         if (M != 0) {
345             if (M > end - start) {
346                 start = "_";
347                 end = start+1;
348             }
349             else if (!Md) {
350                 start = start+M-1;
351                 if (!Mp) {
352                     end = start+1;
353                 }
354             }
355             else {
356                 if (!Mp) {
357                     start = end-M;
358                 }
359                 end = end-M+1;
360             }
361         }
362         vhost_alias_checkspace(r, buf, &dest, end - start);
363         for (p = start; p < end; ++p) {
364             *dest++ = apr_tolower(*p);
365         }
366     }
367     /* no double slashes */
368     if (dest - buf > 0 && dest[-1] == '/') {
369         --dest;
370     }
371     *dest = '\0';
372 
373     if (r->filename)
374         docroot = apr_pstrcat(r->pool, r->filename, buf, NULL);
375     else
376         docroot = apr_pstrdup(r->pool, buf);
377     r->filename = apr_pstrcat(r->pool, docroot, uri, NULL);
378     ap_set_context_info(r, NULL, docroot);
379     ap_set_document_root(r, docroot);
380 }
381 
mva_translate(request_rec * r)382 static int mva_translate(request_rec *r)
383 {
384     mva_sconf_t *conf;
385     const char *name, *map, *uri;
386     mva_mode_e mode;
387     const char *cgi;
388 
389     conf = (mva_sconf_t *) ap_get_module_config(r->server->module_config,
390                                               &vhost_alias_module);
391     cgi = NULL;
392     if (conf->cgi_root) {
393         cgi = strstr(r->uri, "cgi-bin/");
394         if (cgi && (cgi != r->uri + strspn(r->uri, "/"))) {
395             cgi = NULL;
396         }
397     }
398     if (cgi) {
399         mode = conf->cgi_root_mode;
400         map = conf->cgi_root;
401         uri = cgi + strlen("cgi-bin");
402     }
403     else if (r->uri[0] == '/') {
404         mode = conf->doc_root_mode;
405         map = conf->doc_root;
406         uri = r->uri;
407     }
408     else {
409         return DECLINED;
410     }
411 
412     if (mode == VHOST_ALIAS_NAME) {
413         name = ap_get_server_name(r);
414     }
415     else if (mode == VHOST_ALIAS_IP) {
416         name = r->connection->local_ip;
417     }
418     else {
419         return DECLINED;
420     }
421 
422     /* ### There is an optimization available here to determine the
423      * absolute portion of the path from the server config phase,
424      * through the first % segment, and note that portion of the path
425      * canonical_path buffer.
426      */
427     r->canonical_filename = "";
428     vhost_alias_interpolate(r, name, map, uri);
429 
430     if (cgi) {
431         /* see is_scriptaliased() in mod_cgi */
432         r->handler = "cgi-script";
433         apr_table_setn(r->notes, "alias-forced-type", r->handler);
434         ap_set_context_info(r, "/cgi-bin", NULL);
435     }
436 
437     return OK;
438 }
439 
register_hooks(apr_pool_t * p)440 static void register_hooks(apr_pool_t *p)
441 {
442     static const char * const aszPre[]={ "mod_alias.c","mod_userdir.c",NULL };
443 
444     ap_hook_translate_name(mva_translate, aszPre, NULL, APR_HOOK_MIDDLE);
445 }
446 
447 AP_DECLARE_MODULE(vhost_alias) =
448 {
449     STANDARD20_MODULE_STUFF,
450     NULL,                       /* dir config creater */
451     NULL,                       /* dir merger --- default is to override */
452     mva_create_server_config,   /* server config */
453     mva_merge_server_config,    /* merge server configs */
454     mva_commands,               /* command apr_table_t */
455     register_hooks              /* register hooks */
456 };
457 
458