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

Contents of /hydra/src/alias.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.14 - (show annotations)
Wed Oct 2 08:54:09 2002 UTC (21 years, 6 months ago) by nmav
Branch: MAIN
CVS Tags: hydra_0_0_6, hydra_0_0_5
Changes since 1.13: +3 -1 lines
File MIME type: text/plain
Several changes to allow setting the correct query string in HIC cgis.

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

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26