/[hydra]/hydra/src/alias.c
ViewVC logotype

Contents of /hydra/src/alias.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.21 - (show annotations)
Sun Jan 26 11:25:39 2003 UTC (21 years, 2 months ago) by nmav
Branch: MAIN
CVS Tags: hydra_0_1_7, hydra_0_1_6, hydra_0_1_4
Changes since 1.20: +633 -640 lines
File MIME type: text/plain
Better large file support (now uses the included autoconf macros).
(++some indentation)

1 /*
2 * Hydra, an http server
3 * Copyright (C) 1995 Paul Phillips <paulp@go2net.com>
4 * Some changes Copyright (C) 1996 Larry Doolittle <ldoolitt@boa.org>
5 * Some changes Copyright (C) 1996 Russ Nelson <nelson@crynwr.com>
6 * Some changes Copyright (C) 1996-2002 Jon Nelson <jnelson@boa.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 1, or (at your option)
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 *
22 */
23
24 /* $Id: alias.c,v 1.20 2003/01/22 07:51:49 nmav Exp $ */
25
26 #include "boa.h"
27
28 static int init_local_cgi_stuff(request *, int);
29
30 /*
31 * Name: get_hash_value
32 *
33 * Description: adds the ASCII values of the file letters
34 * and mods by the hashtable size to get the hash value
35 * Note: stops at first '/' (or '\0')
36 * The returned value must be %= with the table size.
37 *
38 * This is actually the summing loop. See "boa.h" for the rest.
39 */
40
41 int get_hash_value(const char *str)
42 {
43 unsigned int hash = 0;
44 unsigned int index = 0;
45
46 while (str && str[index] && str[index] != '/')
47 hash += (unsigned int) str[index++];
48 return hash;
49 } /* get_hash_value() */
50
51 /*
52 * Name: add_alias
53 *
54 * Description: add an Alias, Redirect, or ScriptAlias to the
55 * alias hash table.
56 */
57
58 void add_alias(char *hostname, char *fakename, char *realname, int type)
59 {
60 alias *old, *new;
61 int hash, fakelen, reallen, hostlen;
62 virthost *vhost;
63
64 /* sanity checking */
65 if (hostname == NULL || fakename == NULL || realname == NULL) {
66 DIE("NULL values sent to add_alias");
67 }
68
69 hostlen = strlen(hostname);
70 vhost = find_virthost(hostname, hostlen);
71 if (vhost == NULL) {
72 log_error_time();
73 fprintf(stderr, "Tried to add Alias for non-existent host %s.\n",
74 hostname);
75 exit(1);
76 }
77
78 fakelen = strlen(fakename);
79 reallen = strlen(realname);
80 if (fakelen == 0 || reallen == 0) {
81 DIE("empty values sent to add_alias");
82 }
83
84 hash = get_alias_hash_value(fakename);
85 old = vhost->alias_hashtable[hash];
86
87 if (old) {
88 while (old->next) {
89 if (!strcmp(fakename, old->fakename)) /* don't add twice */
90 return;
91 old = old->next;
92 }
93 }
94
95 new = (alias *) malloc(sizeof(alias));
96 if (!new) {
97 DIE("out of memory adding alias to hash");
98 }
99
100 if (old)
101 old->next = new;
102 else
103 vhost->alias_hashtable[hash] = new;
104
105 if (!(new->fakename = strdup(fakename))) {
106 DIE("failed strdup");
107 }
108 new->fake_len = fakelen;
109 /* check for "here" */
110 if (!(new->realname = strdup(realname))) {
111 DIE("strdup of realname failed");
112 }
113 new->real_len = reallen;
114
115 new->type = type;
116 new->next = NULL;
117 } /* add_alias() */
118
119 /*
120 * Name: find_alias
121 *
122 * Description: Locates URI in the alias hashtable if it exists.
123 *
124 * Returns:
125 *
126 * alias structure or NULL if not found
127 */
128
129 alias *find_alias(char *hostname, char *uri, int urilen)
130 {
131 alias *current;
132 int hash;
133 virthost *vhost;
134
135 /* Find ScriptAlias, Alias, or Redirect */
136
137 if ((vhost = find_virthost(hostname, 0)) == NULL) {
138 return NULL;
139 }
140
141 if (urilen == 0)
142 urilen = strlen(uri);
143 hash = get_alias_hash_value(uri);
144
145 current = vhost->alias_hashtable[hash];
146 while (current) {
147 #ifdef FASCIST_LOGGING
148 fprintf(stderr,
149 "%s:%d - comparing \"%s\" (request) to \"%s\" (alias): ",
150 __FILE__, __LINE__, uri, current->fakename);
151 #endif
152 /*
153 * current->fake_len must always be shorter or equal to the URI
154 */
155 /*
156 * when performing matches:
157 * If the virtual part of the url ends in '/', and
158 * we get a match, stop there.
159 * Otherwise, we require '/' or '\0' at the end of the url.
160 * We only check if the virtual path does *not* end in '/'
161 */
162 if (current->fake_len <= urilen &&
163 !memcmp(uri, current->fakename, current->fake_len) &&
164 (current->fakename[current->fake_len - 1] == '/' ||
165 (current->fakename[current->fake_len - 1] != '/' &&
166 (uri[current->fake_len] == '\0' ||
167 uri[current->fake_len] == '/')))) {
168 #ifdef FASCIST_LOGGING
169 fprintf(stderr, "Got it!\n");
170 #endif
171 return current;
172 }
173 #ifdef FASCIST_LOGGING
174 else
175 fprintf(stderr, "Didn't get it!\n");
176 #endif
177 current = current->next;
178 }
179 return current;
180 } /* find_alias() */
181
182 /* Name: is_executable_cgi
183 *
184 * Description: Finds if the given filename is an executable CGI.
185 * if it is one, then it setups some required variables, such as
186 * path_info.
187 *
188 * Returns:
189 * -1 error: probably file was not found
190 * 0 not executable CGI
191 * 1 executable CGI
192 */
193 int is_executable_cgi(request * req, const char *filename)
194 {
195 char *mime_type = get_mime_type(filename);
196 hic_module_st *hic;
197
198 /* below we support cgis outside of a ScriptAlias */
199 if (strcmp(CGI_MIME_TYPE, mime_type) == 0) { /* cgi */
200 int ret;
201 ret =
202 init_local_cgi_stuff(req,
203 (req->http_version ==
204 HTTP_0_9) ? NPH : CGI);
205
206 if (ret == 0)
207 return -1;
208
209 return 1;
210 } else if ((hic = find_hic_appr_module(mime_type, 0)) != 0) { /* HIC CGI */
211 int ret = 0;
212
213 if (hic->action) {
214 req->action = hic->action;
215 if (req->action == NULL)
216 return -1;
217
218 ret = init_local_cgi_stuff(req, CGI_ACTION);
219 }
220 #ifdef ENABLE_HIC
221 else
222 ret = init_local_cgi_stuff(req, HIC_CGI);
223 #endif
224
225 if (ret == 0)
226 return -1;
227
228 return 1;
229 }
230
231 return 0; /* no cgi */
232 }
233
234 /*
235 * Name: translate_uri
236 *
237 * Description: Parse a request's virtual path.
238 * Sets query_string, pathname directly.
239 * Also sets path_info, path_translated, and script_name via
240 * init_script_alias
241 *
242 * Note: NPH in user dir is currently broken
243 *
244 * Note -- this should be broken up.
245 *
246 * Return values:
247 * 0: failure, close it down
248 * 1: success, continue
249 */
250 int translate_uri(request * req)
251 {
252 char buffer[MAX_HEADER_LENGTH + 1];
253 char *req_urip;
254 alias *current;
255 char *p;
256 int uri_len, ret, len; /* FIXME-andreou *//* Goes in pair with the one at L263 */
257
258 req_urip = req->request_uri;
259 if (req_urip[0] != '/') {
260 send_r_bad_request(req);
261 return 0;
262 }
263
264 uri_len = strlen(req->request_uri);
265
266 current = find_alias(req->hostname, req->request_uri, uri_len);
267 if (current) {
268 if (current->type == SCRIPTALIAS) /* Script */
269 return init_script_alias(req, current, uri_len);
270
271 /* not a script alias, therefore begin filling in data */
272 /* FIXME-andreou */
273 /* { */
274 /* int len; */
275 len = current->real_len;
276 len += uri_len - current->fake_len;
277 if (len > MAX_HEADER_LENGTH) {
278 log_error_doc(req);
279 fputs("URI too long!\n", stderr);
280 send_r_bad_request(req);
281 return 0;
282 }
283 memcpy(buffer, current->realname, current->real_len);
284 memcpy(buffer + current->real_len,
285 req->request_uri + current->fake_len,
286 uri_len - current->fake_len + 1);
287 /* } */
288
289 if (current->type == REDIRECT) { /* Redirect */
290 if (req->method == M_POST) { /* POST to non-script */
291 /* it's not a cgi, but we try to POST??? */
292 send_r_bad_request(req);
293 return 0; /* not a script alias, therefore begin filling in data */
294 }
295 send_r_moved_temp(req, buffer, "");
296 return 0;
297 } else { /* Alias */
298 req->pathname = strdup(buffer);
299 if (!req->pathname) {
300 send_r_error(req);
301 WARN("unable to strdup buffer onto req->pathname");
302 return 0;
303 }
304 return 1;
305 }
306 }
307
308 /* The reason why this is *not* an 'else if' is that,
309 * after aliasing, we still have to check for '~' expansion
310 */
311
312 if (req->user_dir[0] != 0 && req->request_uri[1] == '~') {
313 char *user_homedir;
314
315 req_urip = req->request_uri + 2;
316
317 /* since we have uri_len which is from strlen(req->request_uri) */
318 p = memchr(req_urip, '/', uri_len - 2);
319 if (p)
320 *p = '\0';
321
322 user_homedir = get_home_dir(req_urip);
323 if (p) /* have to restore request_uri in case of error */
324 *p = '/';
325
326 if (!user_homedir) { /* no such user */
327 send_r_not_found(req);
328 return 0;
329 }
330 {
331 int l1 = strlen(user_homedir);
332 int l2 = strlen(req->user_dir);
333 int l3 = (p ? strlen(p) : 0);
334
335 if (l1 + l2 + l3 + 1 > MAX_HEADER_LENGTH) {
336 log_error_doc(req);
337 fputs("uri too long!\n", stderr);
338 send_r_bad_request(req);
339 return 0;
340 }
341
342 memcpy(buffer, user_homedir, l1);
343 buffer[l1] = '/';
344 memcpy(buffer + l1 + 1, req->user_dir, l2 + 1);
345 if (p)
346 memcpy(buffer + l1 + 1 + l2, p, l3 + 1);
347 }
348 } else if (req->document_root[0] != 0) {
349 /* no aliasing, no userdir... */
350 int l1, l2;
351
352 l1 = strlen(req->document_root);
353 l2 = strlen(req->request_uri);
354
355 if (l1 + l2 + 1 > MAX_HEADER_LENGTH) {
356 log_error_doc(req);
357 fputs("uri too long!\n", stderr);
358 send_r_bad_request(req);
359 return 0;
360 }
361
362 /* the 'l2 + 1' is there so we copy the '\0' as well */
363 memcpy(buffer, req->document_root, l1);
364 memcpy(buffer + l1, req->request_uri, l2 + 1);
365 } else {
366 /* not aliased. not userdir. not part of document_root. BAIL */
367 send_r_bad_request(req);
368 return 0;
369 }
370
371 /* if here,
372 * o it may be aliased but it's not a redirect or a script...
373 * o it may be a homedir
374 * o it may be a document_root resource (with or without virtual host)
375 */
376
377 req->pathname = strdup(buffer);
378 if (!req->pathname) {
379 WARN("Could not strdup buffer for req->pathname!");
380 send_r_error(req);
381 return 0;
382 }
383
384 /* FIXME -- script_name here equals req->request_uri */
385 /* script_name could end up as /cgi-bin/bob/extra_path */
386
387 if ((ret = is_executable_cgi(req, buffer)) != 0) {
388 if (ret == -1) {
389 send_r_not_found(req);
390 return 0;
391 }
392 return 1;
393 } else if (req->method == M_POST) { /* POST to non-script */
394 /* it's not a cgi, but we try to POST??? */
395 send_r_bad_request(req);
396 return 0;
397 } else /* we are done!! */
398 return 1;
399 } /* translate_uri() */
400
401 /* Sets the parameters needed in CGIs. This is used in CGIs
402 * which are not in aliased directories.
403 *
404 * Returns:
405 * 0 Some error.
406 * 1 ok.
407 */
408 static int init_local_cgi_stuff(request * req, int cgi)
409 {
410 char pathname[MAX_HEADER_LENGTH + 1];
411 char *p;
412 int perms;
413
414 #ifdef FASCIST_LOGGING
415 log_error_time();
416 fprintf(stderr, "%s:%d - buffer is: \"%s\"\n",
417 __FILE__, __LINE__, buffer);
418 #endif
419
420 if (req->script_name)
421 free(req->script_name);
422 req->script_name = strdup(req->request_uri);
423 if (!req->script_name) {
424 WARN("Could not strdup req->request_uri for req->script_name");
425 return 0;
426 }
427
428 req->is_cgi = cgi;
429
430 /* For PATH_INFO, and PATH_TRANSLATED */
431 strcpy(pathname, req->request_uri);
432 p = strrchr(pathname, '/');
433 if (p)
434 *p = 0;
435
436
437
438 if (req->path_info)
439 free(req->path_info);
440 req->path_info = strdup(pathname);
441 if (!req->path_info) {
442 WARN("unable to strdup pathname for req->path_info");
443 return 0;
444 }
445
446 strcpy(pathname, req->document_root);
447 strcat(pathname, req->request_uri);
448
449 if (req->path_translated)
450 free(req->path_translated);
451 req->path_translated = strdup(pathname);
452 if (!req->path_translated) {
453 WARN("unable to strdup pathname for req->path_translated");
454 return 0;
455 }
456
457 /* Check if the file is accesible */
458 if (cgi == HIC_CGI || cgi == CGI_ACTION)
459 perms = R_OK;
460 else
461 perms = R_OK | X_OK;
462
463 if (access(req->path_translated, perms) != 0) {
464 if (cgi == CGI) {
465 log_error_time();
466 fprintf(stderr, "Script '%s' is not executable.\n",
467 req->path_translated);
468 }
469 /* couldn't access file */
470 return 0;
471 }
472
473 return 1;
474 }
475
476 /*
477 * Name: init_script_alias
478 *
479 * Description: Performs full parsing on a ScriptAlias request
480 * Sets path_info and script_name
481 *
482 * Return values:
483 *
484 * 0: failure, shut down
485 * 1: success, continue
486 */
487 int init_script_alias(request * req, alias * current1, int uri_len)
488 {
489 char pathname[MAX_HEADER_LENGTH + 1];
490 struct stat statbuf;
491 char buffer[MAX_HEADER_LENGTH + 1];
492
493 int index = 0;
494 char c;
495 int err;
496 virthost *vhost;
497
498 vhost = find_virthost(req->hostname, 0);
499 if (vhost == NULL) {
500 send_r_not_found(req);
501 return 0;
502 }
503
504 /* copies the "real" path + the non-alias portion of the
505 uri to pathname.
506 */
507
508 if (uri_len - current1->fake_len + current1->real_len >
509 MAX_HEADER_LENGTH) {
510 log_error_doc(req);
511 fputs("uri too long!\n", stderr);
512 send_r_bad_request(req);
513 return 0;
514 }
515
516 memcpy(pathname, current1->realname, current1->real_len);
517 memcpy(pathname + current1->real_len, &req->request_uri[current1->fake_len], uri_len - current1->fake_len + 1); /* the +1 copies the NUL */
518 #ifdef FASCIST_LOGGING
519 log_error_time();
520 fprintf(stderr,
521 "%s:%d - pathname in init_script_alias is: \"%s\" (\"%s\")\n",
522 __FILE__, __LINE__, pathname, pathname + current1->real_len);
523 #endif
524 if (strncmp("nph-", pathname + current1->real_len, 4) == 0
525 || req->http_version == HTTP_0_9)
526 req->is_cgi = NPH;
527 else
528 req->is_cgi = CGI;
529
530
531 /* start at the beginning of the actual uri...
532 (in /cgi-bin/bob, start at the 'b' in bob */
533 index = current1->real_len;
534
535 /* go to first and successive '/' and keep checking
536 * if it is a full pathname
537 * on success (stat (not lstat) of file is a *regular file*)
538 */
539 do {
540 c = pathname[++index];
541 if (c == '/') {
542 pathname[index] = '\0';
543 err = stat(pathname, &statbuf);
544 pathname[index] = '/';
545 if (err == -1) {
546 send_r_not_found(req);
547 return 0;
548 }
549
550 /* is it a dir? */
551 if (!S_ISDIR(statbuf.st_mode)) {
552 /* check access */
553 if (!(statbuf.st_mode & (S_IFREG | /* regular file */
554 (S_IRUSR | S_IXUSR) | /* u+rx */
555 (S_IRGRP | S_IXGRP) | /* g+rx */
556 (S_IROTH | S_IXOTH)))) { /* o+rx */
557 send_r_forbidden(req);
558 return 0;
559 }
560 /* stop here */
561 break;
562 }
563 }
564 } while (c != '\0');
565
566
567 /* start at the beginning of the actual uri...
568 * (in /cgi-bin/bob, start at the 'b' in bob
569 */
570 index = current1->real_len;
571
572 /* go to first and successive '/' and keep checking
573 * if it is a full pathname
574 * on success (stat (not lstat) of file is a *regular file*)
575 */
576 do {
577 c = pathname[++index];
578 if (c == '/') {
579 pathname[index] = '\0';
580 err = stat(pathname, &statbuf);
581 pathname[index] = '/';
582 if (err == -1) {
583 send_r_not_found(req);
584 return 0;
585 }
586
587 /* is it a dir? */
588 if (!S_ISDIR(statbuf.st_mode)) {
589 /* check access */
590 if (!(statbuf.st_mode & (S_IFREG | /* regular file */
591 (S_IRUSR | S_IXUSR) | /* u+rx */
592 (S_IRGRP | S_IXGRP) | /* g+rx */
593 (S_IROTH | S_IXOTH)))) { /* o+rx */
594 send_r_forbidden(req);
595 return 0;
596 }
597 /* stop here */
598 break;
599 }
600 }
601 } while (c != '\0');
602
603 req->script_name = strdup(req->request_uri);
604 if (!req->script_name) {
605 send_r_error(req);
606 WARN("unable to strdup req->request_uri for req->script_name");
607 return 0;
608 }
609
610 if (c == '\0') {
611 err = stat(pathname, &statbuf);
612 if (err == -1) {
613 send_r_not_found(req);
614 return 0;
615 }
616
617 /* is it a dir? */
618 if (!S_ISDIR(statbuf.st_mode)) {
619 /* check access */
620 if (!(statbuf.st_mode & (S_IFREG | /* regular file */
621 (S_IRUSR | S_IXUSR) | /* u+rx */
622 (S_IRGRP | S_IXGRP) | /* g+rx */
623 (S_IROTH | S_IXOTH)))) { /* o+rx */
624 send_r_forbidden(req);
625 return 0;
626 }
627 /* stop here */
628 } else {
629 send_r_forbidden(req);
630 return 0;
631 }
632 }
633
634 /* we have path_info if c == '/'... still have to check for query */
635 else if (c == '/') {
636 int hash;
637 alias *current;
638 int path_len;
639
640 req->path_info = strdup(pathname + index);
641 if (!req->path_info) {
642 send_r_error(req);
643 WARN("unable to strdup pathname + index for req->path_info");
644 return 0;
645 }
646 pathname[index] = '\0'; /* strip path_info from path */
647 path_len = strlen(req->path_info);
648 /* we need to fix script_name here */
649 /* index points into pathname, which is
650 * realname/cginame/foo
651 * and index points to the '/foo' part
652 */
653 req->script_name[strlen(req->script_name) - path_len] = '\0'; /* zap off the /foo part */
654
655 /* FIXME-andreou */
656 /* now, we have to re-alias the extra path info....
657 * this sucks.
658 */
659 hash = get_alias_hash_value(req->path_info);
660 current = vhost->alias_hashtable[hash];
661 while (current && !req->path_translated) {
662 if (!strncmp(req->path_info, current->fakename,
663 current->fake_len)) {
664
665 if (current->real_len +
666 path_len - current->fake_len > MAX_HEADER_LENGTH) {
667 log_error_doc(req);
668 fputs("uri too long!\n", stderr);
669 send_r_bad_request(req);
670 return 0;
671 }
672
673 memcpy(buffer, current->realname, current->real_len);
674 strcpy(buffer + current->real_len,
675 &req->path_info[current->fake_len]);
676 req->path_translated = strdup(buffer);
677 if (!req->path_translated) {
678 send_r_error(req);
679 WARN("unable to strdup buffer for req->path_translated");
680 return 0;
681 }
682 }
683 current = current->next;
684 }
685 /* no alias... try userdir */
686 if (!req->path_translated && req->user_dir[0] != 0
687 && req->path_info[1] == '~') {
688 char *user_homedir;
689 char *p;
690
691 p = strchr(pathname + index + 1, '/');
692 if (p)
693 *p = '\0';
694
695 user_homedir = get_home_dir(pathname + index + 2);
696 if (p)
697 *p = '/';
698
699 if (!user_homedir) { /* no such user */
700 send_r_not_found(req);
701 return 0;
702 }
703 {
704 int l1 = strlen(user_homedir);
705 int l2 = strlen(req->user_dir);
706 int l3;
707 if (p)
708 l3 = strlen(p);
709 else
710 l3 = 0;
711
712 req->path_translated = malloc(l1 + l2 + l3 + 2);
713 if (req->path_translated == NULL) {
714 send_r_error(req);
715 WARN("unable to malloc memory for req->path_translated");
716 return 0;
717 }
718 memcpy(req->path_translated, user_homedir, l1);
719 req->path_translated[l1] = '/';
720 memcpy(req->path_translated + l1 + 1, req->user_dir, l2 + 1);
721 if (p)
722 memcpy(req->path_translated + l1 + 1 + l2, p, l3 + 1);
723 }
724 }
725 if (!req->path_translated && req->document_root[0] != 0) {
726 /* no userdir, no aliasing... try document root */
727 int l1, l2;
728 l1 = strlen(req->document_root);
729 l2 = path_len;
730
731 req->path_translated = malloc(l1 + l2 + 1);
732 if (req->path_translated == NULL) {
733 send_r_error(req);
734 WARN("unable to malloc memory for req->path_translated");
735 return 0;
736 }
737 memcpy(req->path_translated, req->document_root, l1);
738 memcpy(req->path_translated + l1, req->path_info, l2 + 1);
739 }
740 }
741
742 req->pathname = strdup(pathname);
743 if (!req->pathname) {
744 send_r_error(req);
745 WARN("unable to strdup pathname for req->pathname");
746 return 0;
747 }
748
749 return 1;
750 } /* init_script_alias() */
751
752 /*
753 * Empties the alias hashtable, deallocating any allocated memory.
754 */
755
756 void dump_alias(virthost * vhost)
757 {
758 int i;
759 alias *temp;
760
761 for (i = 0; i < ALIAS_HASHTABLE_SIZE; ++i) { /* these limits OK? */
762 if (vhost->alias_hashtable[i]) {
763 temp = vhost->alias_hashtable[i];
764 while (temp) {
765 alias *temp_next;
766
767 if (temp->fakename)
768 free(temp->fakename);
769 if (temp->realname)
770 free(temp->realname);
771 temp_next = temp->next;
772 free(temp);
773 temp = temp_next;
774 }
775 vhost->alias_hashtable[i] = NULL;
776 }
777 }
778 } /* dump_alias() */
779
780 /* EOF */

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26