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

Contents of /hydra/src/alias.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.18.2.6 - (show annotations)
Fri Jan 17 17:19:15 2003 UTC (21 years, 3 months ago) by nmav
Branch: hydra_0_1_0_patches
CVS Tags: hydra_0_1_3
Changes since 1.18.2.5: +3 -3 lines
File MIME type: text/plain
Droped req->simple, and added req->http_version. The old req->http_version was moved to req->http_version_str.

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

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26