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

Contents of /hydra/src/alias.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.22 - (show annotations)
Thu Mar 9 18:11:07 2006 UTC (18 years, 1 month ago) by nmav
Branch: MAIN
CVS Tags: hydra_0_1_6_without_hic, hydra_0_1_8, HEAD
Changes since 1.21: +4 -8 lines
File MIME type: text/plain
Removed the HIC support.

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.21 2003/01/26 11:25:39 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 action_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_cgi_action_appr_module(mime_type, 0)) != 0) { /* CGI ACTION */
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
221 if (ret == 0)
222 return -1;
223
224 return 1;
225 }
226
227 return 0; /* no cgi */
228 }
229
230 /*
231 * Name: translate_uri
232 *
233 * Description: Parse a request's virtual path.
234 * Sets query_string, pathname directly.
235 * Also sets path_info, path_translated, and script_name via
236 * init_script_alias
237 *
238 * Note: NPH in user dir is currently broken
239 *
240 * Note -- this should be broken up.
241 *
242 * Return values:
243 * 0: failure, close it down
244 * 1: success, continue
245 */
246 int translate_uri(request * req)
247 {
248 char buffer[MAX_HEADER_LENGTH + 1];
249 char *req_urip;
250 alias *current;
251 char *p;
252 int uri_len, ret, len; /* FIXME-andreou *//* Goes in pair with the one at L263 */
253
254 req_urip = req->request_uri;
255 if (req_urip[0] != '/') {
256 send_r_bad_request(req);
257 return 0;
258 }
259
260 uri_len = strlen(req->request_uri);
261
262 current = find_alias(req->hostname, req->request_uri, uri_len);
263 if (current) {
264 if (current->type == SCRIPTALIAS) /* Script */
265 return init_script_alias(req, current, uri_len);
266
267 /* not a script alias, therefore begin filling in data */
268 /* FIXME-andreou */
269 /* { */
270 /* int len; */
271 len = current->real_len;
272 len += uri_len - current->fake_len;
273 if (len > MAX_HEADER_LENGTH) {
274 log_error_doc(req);
275 fputs("URI too long!\n", stderr);
276 send_r_bad_request(req);
277 return 0;
278 }
279 memcpy(buffer, current->realname, current->real_len);
280 memcpy(buffer + current->real_len,
281 req->request_uri + current->fake_len,
282 uri_len - current->fake_len + 1);
283 /* } */
284
285 if (current->type == REDIRECT) { /* Redirect */
286 if (req->method == M_POST) { /* POST to non-script */
287 /* it's not a cgi, but we try to POST??? */
288 send_r_bad_request(req);
289 return 0; /* not a script alias, therefore begin filling in data */
290 }
291 send_r_moved_temp(req, buffer, "");
292 return 0;
293 } else { /* Alias */
294 req->pathname = strdup(buffer);
295 if (!req->pathname) {
296 send_r_error(req);
297 WARN("unable to strdup buffer onto req->pathname");
298 return 0;
299 }
300 return 1;
301 }
302 }
303
304 /* The reason why this is *not* an 'else if' is that,
305 * after aliasing, we still have to check for '~' expansion
306 */
307
308 if (req->user_dir[0] != 0 && req->request_uri[1] == '~') {
309 char *user_homedir;
310
311 req_urip = req->request_uri + 2;
312
313 /* since we have uri_len which is from strlen(req->request_uri) */
314 p = memchr(req_urip, '/', uri_len - 2);
315 if (p)
316 *p = '\0';
317
318 user_homedir = get_home_dir(req_urip);
319 if (p) /* have to restore request_uri in case of error */
320 *p = '/';
321
322 if (!user_homedir) { /* no such user */
323 send_r_not_found(req);
324 return 0;
325 }
326 {
327 int l1 = strlen(user_homedir);
328 int l2 = strlen(req->user_dir);
329 int l3 = (p ? strlen(p) : 0);
330
331 if (l1 + l2 + l3 + 1 > MAX_HEADER_LENGTH) {
332 log_error_doc(req);
333 fputs("uri too long!\n", stderr);
334 send_r_bad_request(req);
335 return 0;
336 }
337
338 memcpy(buffer, user_homedir, l1);
339 buffer[l1] = '/';
340 memcpy(buffer + l1 + 1, req->user_dir, l2 + 1);
341 if (p)
342 memcpy(buffer + l1 + 1 + l2, p, l3 + 1);
343 }
344 } else if (req->document_root[0] != 0) {
345 /* no aliasing, no userdir... */
346 int l1, l2;
347
348 l1 = strlen(req->document_root);
349 l2 = strlen(req->request_uri);
350
351 if (l1 + l2 + 1 > MAX_HEADER_LENGTH) {
352 log_error_doc(req);
353 fputs("uri too long!\n", stderr);
354 send_r_bad_request(req);
355 return 0;
356 }
357
358 /* the 'l2 + 1' is there so we copy the '\0' as well */
359 memcpy(buffer, req->document_root, l1);
360 memcpy(buffer + l1, req->request_uri, l2 + 1);
361 } else {
362 /* not aliased. not userdir. not part of document_root. BAIL */
363 send_r_bad_request(req);
364 return 0;
365 }
366
367 /* if here,
368 * o it may be aliased but it's not a redirect or a script...
369 * o it may be a homedir
370 * o it may be a document_root resource (with or without virtual host)
371 */
372
373 req->pathname = strdup(buffer);
374 if (!req->pathname) {
375 WARN("Could not strdup buffer for req->pathname!");
376 send_r_error(req);
377 return 0;
378 }
379
380 /* FIXME -- script_name here equals req->request_uri */
381 /* script_name could end up as /cgi-bin/bob/extra_path */
382
383 if ((ret = is_executable_cgi(req, buffer)) != 0) {
384 if (ret == -1) {
385 send_r_not_found(req);
386 return 0;
387 }
388 return 1;
389 } else if (req->method == M_POST) { /* POST to non-script */
390 /* it's not a cgi, but we try to POST??? */
391 send_r_bad_request(req);
392 return 0;
393 } else /* we are done!! */
394 return 1;
395 } /* translate_uri() */
396
397 /* Sets the parameters needed in CGIs. This is used in CGIs
398 * which are not in aliased directories.
399 *
400 * Returns:
401 * 0 Some error.
402 * 1 ok.
403 */
404 static int init_local_cgi_stuff(request * req, int cgi)
405 {
406 char pathname[MAX_HEADER_LENGTH + 1];
407 char *p;
408 int perms;
409
410 #ifdef FASCIST_LOGGING
411 log_error_time();
412 fprintf(stderr, "%s:%d - buffer is: \"%s\"\n",
413 __FILE__, __LINE__, buffer);
414 #endif
415
416 if (req->script_name)
417 free(req->script_name);
418 req->script_name = strdup(req->request_uri);
419 if (!req->script_name) {
420 WARN("Could not strdup req->request_uri for req->script_name");
421 return 0;
422 }
423
424 req->is_cgi = cgi;
425
426 /* For PATH_INFO, and PATH_TRANSLATED */
427 strcpy(pathname, req->request_uri);
428 p = strrchr(pathname, '/');
429 if (p)
430 *p = 0;
431
432
433
434 if (req->path_info)
435 free(req->path_info);
436 req->path_info = strdup(pathname);
437 if (!req->path_info) {
438 WARN("unable to strdup pathname for req->path_info");
439 return 0;
440 }
441
442 strcpy(pathname, req->document_root);
443 strcat(pathname, req->request_uri);
444
445 if (req->path_translated)
446 free(req->path_translated);
447 req->path_translated = strdup(pathname);
448 if (!req->path_translated) {
449 WARN("unable to strdup pathname for req->path_translated");
450 return 0;
451 }
452
453 /* Check if the file is accesible */
454 if (cgi == CGI_ACTION)
455 perms = R_OK;
456 else
457 perms = R_OK | X_OK;
458
459 if (access(req->path_translated, perms) != 0) {
460 if (cgi == CGI) {
461 log_error_time();
462 fprintf(stderr, "Script '%s' is not executable.\n",
463 req->path_translated);
464 }
465 /* couldn't access file */
466 return 0;
467 }
468
469 return 1;
470 }
471
472 /*
473 * Name: init_script_alias
474 *
475 * Description: Performs full parsing on a ScriptAlias request
476 * Sets path_info and script_name
477 *
478 * Return values:
479 *
480 * 0: failure, shut down
481 * 1: success, continue
482 */
483 int init_script_alias(request * req, alias * current1, int uri_len)
484 {
485 char pathname[MAX_HEADER_LENGTH + 1];
486 struct stat statbuf;
487 char buffer[MAX_HEADER_LENGTH + 1];
488
489 int index = 0;
490 char c;
491 int err;
492 virthost *vhost;
493
494 vhost = find_virthost(req->hostname, 0);
495 if (vhost == NULL) {
496 send_r_not_found(req);
497 return 0;
498 }
499
500 /* copies the "real" path + the non-alias portion of the
501 uri to pathname.
502 */
503
504 if (uri_len - current1->fake_len + current1->real_len >
505 MAX_HEADER_LENGTH) {
506 log_error_doc(req);
507 fputs("uri too long!\n", stderr);
508 send_r_bad_request(req);
509 return 0;
510 }
511
512 memcpy(pathname, current1->realname, current1->real_len);
513 memcpy(pathname + current1->real_len, &req->request_uri[current1->fake_len], uri_len - current1->fake_len + 1); /* the +1 copies the NUL */
514 #ifdef FASCIST_LOGGING
515 log_error_time();
516 fprintf(stderr,
517 "%s:%d - pathname in init_script_alias is: \"%s\" (\"%s\")\n",
518 __FILE__, __LINE__, pathname, pathname + current1->real_len);
519 #endif
520 if (strncmp("nph-", pathname + current1->real_len, 4) == 0
521 || req->http_version == HTTP_0_9)
522 req->is_cgi = NPH;
523 else
524 req->is_cgi = CGI;
525
526
527 /* start at the beginning of the actual uri...
528 (in /cgi-bin/bob, start at the 'b' in bob */
529 index = current1->real_len;
530
531 /* go to first and successive '/' and keep checking
532 * if it is a full pathname
533 * on success (stat (not lstat) of file is a *regular file*)
534 */
535 do {
536 c = pathname[++index];
537 if (c == '/') {
538 pathname[index] = '\0';
539 err = stat(pathname, &statbuf);
540 pathname[index] = '/';
541 if (err == -1) {
542 send_r_not_found(req);
543 return 0;
544 }
545
546 /* is it a dir? */
547 if (!S_ISDIR(statbuf.st_mode)) {
548 /* check access */
549 if (!(statbuf.st_mode & (S_IFREG | /* regular file */
550 (S_IRUSR | S_IXUSR) | /* u+rx */
551 (S_IRGRP | S_IXGRP) | /* g+rx */
552 (S_IROTH | S_IXOTH)))) { /* o+rx */
553 send_r_forbidden(req);
554 return 0;
555 }
556 /* stop here */
557 break;
558 }
559 }
560 } while (c != '\0');
561
562
563 /* start at the beginning of the actual uri...
564 * (in /cgi-bin/bob, start at the 'b' in bob
565 */
566 index = current1->real_len;
567
568 /* go to first and successive '/' and keep checking
569 * if it is a full pathname
570 * on success (stat (not lstat) of file is a *regular file*)
571 */
572 do {
573 c = pathname[++index];
574 if (c == '/') {
575 pathname[index] = '\0';
576 err = stat(pathname, &statbuf);
577 pathname[index] = '/';
578 if (err == -1) {
579 send_r_not_found(req);
580 return 0;
581 }
582
583 /* is it a dir? */
584 if (!S_ISDIR(statbuf.st_mode)) {
585 /* check access */
586 if (!(statbuf.st_mode & (S_IFREG | /* regular file */
587 (S_IRUSR | S_IXUSR) | /* u+rx */
588 (S_IRGRP | S_IXGRP) | /* g+rx */
589 (S_IROTH | S_IXOTH)))) { /* o+rx */
590 send_r_forbidden(req);
591 return 0;
592 }
593 /* stop here */
594 break;
595 }
596 }
597 } while (c != '\0');
598
599 req->script_name = strdup(req->request_uri);
600 if (!req->script_name) {
601 send_r_error(req);
602 WARN("unable to strdup req->request_uri for req->script_name");
603 return 0;
604 }
605
606 if (c == '\0') {
607 err = stat(pathname, &statbuf);
608 if (err == -1) {
609 send_r_not_found(req);
610 return 0;
611 }
612
613 /* is it a dir? */
614 if (!S_ISDIR(statbuf.st_mode)) {
615 /* check access */
616 if (!(statbuf.st_mode & (S_IFREG | /* regular file */
617 (S_IRUSR | S_IXUSR) | /* u+rx */
618 (S_IRGRP | S_IXGRP) | /* g+rx */
619 (S_IROTH | S_IXOTH)))) { /* o+rx */
620 send_r_forbidden(req);
621 return 0;
622 }
623 /* stop here */
624 } else {
625 send_r_forbidden(req);
626 return 0;
627 }
628 }
629
630 /* we have path_info if c == '/'... still have to check for query */
631 else if (c == '/') {
632 int hash;
633 alias *current;
634 int path_len;
635
636 req->path_info = strdup(pathname + index);
637 if (!req->path_info) {
638 send_r_error(req);
639 WARN("unable to strdup pathname + index for req->path_info");
640 return 0;
641 }
642 pathname[index] = '\0'; /* strip path_info from path */
643 path_len = strlen(req->path_info);
644 /* we need to fix script_name here */
645 /* index points into pathname, which is
646 * realname/cginame/foo
647 * and index points to the '/foo' part
648 */
649 req->script_name[strlen(req->script_name) - path_len] = '\0'; /* zap off the /foo part */
650
651 /* FIXME-andreou */
652 /* now, we have to re-alias the extra path info....
653 * this sucks.
654 */
655 hash = get_alias_hash_value(req->path_info);
656 current = vhost->alias_hashtable[hash];
657 while (current && !req->path_translated) {
658 if (!strncmp(req->path_info, current->fakename,
659 current->fake_len)) {
660
661 if (current->real_len +
662 path_len - current->fake_len > MAX_HEADER_LENGTH) {
663 log_error_doc(req);
664 fputs("uri too long!\n", stderr);
665 send_r_bad_request(req);
666 return 0;
667 }
668
669 memcpy(buffer, current->realname, current->real_len);
670 strcpy(buffer + current->real_len,
671 &req->path_info[current->fake_len]);
672 req->path_translated = strdup(buffer);
673 if (!req->path_translated) {
674 send_r_error(req);
675 WARN("unable to strdup buffer for req->path_translated");
676 return 0;
677 }
678 }
679 current = current->next;
680 }
681 /* no alias... try userdir */
682 if (!req->path_translated && req->user_dir[0] != 0
683 && req->path_info[1] == '~') {
684 char *user_homedir;
685 char *p;
686
687 p = strchr(pathname + index + 1, '/');
688 if (p)
689 *p = '\0';
690
691 user_homedir = get_home_dir(pathname + index + 2);
692 if (p)
693 *p = '/';
694
695 if (!user_homedir) { /* no such user */
696 send_r_not_found(req);
697 return 0;
698 }
699 {
700 int l1 = strlen(user_homedir);
701 int l2 = strlen(req->user_dir);
702 int l3;
703 if (p)
704 l3 = strlen(p);
705 else
706 l3 = 0;
707
708 req->path_translated = malloc(l1 + l2 + l3 + 2);
709 if (req->path_translated == NULL) {
710 send_r_error(req);
711 WARN("unable to malloc memory for req->path_translated");
712 return 0;
713 }
714 memcpy(req->path_translated, user_homedir, l1);
715 req->path_translated[l1] = '/';
716 memcpy(req->path_translated + l1 + 1, req->user_dir, l2 + 1);
717 if (p)
718 memcpy(req->path_translated + l1 + 1 + l2, p, l3 + 1);
719 }
720 }
721 if (!req->path_translated && req->document_root[0] != 0) {
722 /* no userdir, no aliasing... try document root */
723 int l1, l2;
724 l1 = strlen(req->document_root);
725 l2 = path_len;
726
727 req->path_translated = malloc(l1 + l2 + 1);
728 if (req->path_translated == NULL) {
729 send_r_error(req);
730 WARN("unable to malloc memory for req->path_translated");
731 return 0;
732 }
733 memcpy(req->path_translated, req->document_root, l1);
734 memcpy(req->path_translated + l1, req->path_info, l2 + 1);
735 }
736 }
737
738 req->pathname = strdup(pathname);
739 if (!req->pathname) {
740 send_r_error(req);
741 WARN("unable to strdup pathname for req->pathname");
742 return 0;
743 }
744
745 return 1;
746 } /* init_script_alias() */
747
748 /*
749 * Empties the alias hashtable, deallocating any allocated memory.
750 */
751
752 void dump_alias(virthost * vhost)
753 {
754 int i;
755 alias *temp;
756
757 for (i = 0; i < ALIAS_HASHTABLE_SIZE; ++i) { /* these limits OK? */
758 if (vhost->alias_hashtable[i]) {
759 temp = vhost->alias_hashtable[i];
760 while (temp) {
761 alias *temp_next;
762
763 if (temp->fakename)
764 free(temp->fakename);
765 if (temp->realname)
766 free(temp->realname);
767 temp_next = temp->next;
768 free(temp);
769 temp = temp_next;
770 }
771 vhost->alias_hashtable[i] = NULL;
772 }
773 }
774 } /* dump_alias() */
775
776 /* EOF */

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26