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

Contents of /hydra/src/alias.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.2 - (show annotations)
Sun Sep 22 09:07:57 2002 UTC (21 years, 7 months ago) by nmav
Branch: MAIN
CVS Tags: BOAS_WITH_RANGES_AND_CGI
Changes since 1.1: +2 -2 lines
File MIME type: text/plain
Several improvements and reentrancy fixes.

1 /*
2 * Boa, 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.1.1.1 2002/09/21 13:53:54 nmav Exp $ */
25
26 #include "boa.h"
27
28 static alias *alias_hashtable[ALIAS_HASHTABLE_SIZE];
29
30 int get_alias_hash_value(char *file);
31
32 /*
33 * Name: get_alias_hash_value
34 *
35 * Description: adds the ASCII values of the file letters
36 * and mods by the hashtable size to get the hash value
37 * Note: stops at first '/' (or '\0')
38 */
39
40 int get_alias_hash_value(char *file)
41 {
42 unsigned int hash = 0;
43 unsigned int index = 0;
44 unsigned char c;
45
46 hash = file[index++];
47 while ((c = file[index++]) && c != '/')
48 hash += (unsigned int) c;
49
50 return hash % ALIAS_HASHTABLE_SIZE;
51 }
52
53 /*
54 * Name: add_alias
55 *
56 * Description: add an Alias, Redirect, or ScriptAlias to the
57 * alias hash table.
58 */
59
60 void add_alias(char *fakename, char *realname, int type)
61 {
62 int hash;
63 alias *old, *new;
64 int fakelen, reallen;
65
66 /* sanity checking */
67 if (fakename == NULL || realname == NULL) {
68 DIE("NULL values sent to add_alias");
69 }
70
71 fakelen = strlen(fakename);
72 reallen = strlen(realname);
73 if (fakelen == 0 || reallen == 0) {
74 DIE("empty values sent to add_alias");
75 }
76
77 hash = get_alias_hash_value(fakename);
78
79 old = alias_hashtable[hash];
80
81 if (old) {
82 while (old->next) {
83 if (!strcmp(fakename, old->fakename)) /* don't add twice */
84 return;
85 old = old->next;
86 }
87 }
88
89 new = (alias *) malloc(sizeof (alias));
90 if (!new) {
91 DIE("out of memory adding alias to hash");
92 }
93
94 if (old)
95 old->next = new;
96 else
97 alias_hashtable[hash] = new;
98
99 new->fakename = strdup(fakename);
100 if (!new->fakename) {
101 DIE("failed strdup");
102 }
103 new->fake_len = fakelen;
104 /* check for "here" */
105 if (realname[0] == '.' && realname[1] == '/') {
106 new->realname = normalize_path(realname+2);
107 if (!new->realname) {
108 /* superfluous - normalize_path checks for NULL return values. */
109 DIE("normalize_path returned NULL");
110 }
111 reallen = strlen(new->realname);
112 } else {
113 new->realname = strdup(realname);
114 if (!new->realname) {
115 DIE("strdup of realname failed");
116 }
117 }
118 new->real_len = reallen;
119
120 new->type = type;
121 new->next = NULL;
122 }
123
124 /*
125 * Name: find_alias
126 *
127 * Description: Locates uri in the alias hashtable if it exists.
128 *
129 * Returns:
130 *
131 * alias structure or NULL if not found
132 */
133
134 alias *find_alias(char *uri, int urilen)
135 {
136 alias *current;
137 int hash;
138
139 /* Find ScriptAlias, Alias, or Redirect */
140
141 if (urilen == 0)
142 urilen = strlen(uri);
143 hash = get_alias_hash_value(uri);
144
145 current = 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 /* current->fake_len must always be:
153 * 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, "Don't Got it!\n");
176 #endif
177 current = current->next;
178 }
179 return current;
180 }
181
182 /*
183 * Name: translate_uri
184 *
185 * Description: Parse a request's virtual path.
186 * Sets query_string, pathname directly.
187 * Also sets path_info, path_translated, and script_name via
188 * init_script_alias
189 *
190 * Note: NPH in user dir is currently broken
191 *
192 * Note -- this should be broken up.
193 *
194 * Return values:
195 * 0: failure, close it down
196 * 1: success, continue
197 */
198
199 int translate_uri(request * req)
200 {
201 char buffer[MAX_HEADER_LENGTH + 1];
202 char *req_urip;
203 alias *current;
204 char *p;
205 int uri_len;
206
207 req_urip = req->request_uri;
208 if (req_urip[0] != '/') {
209 send_r_bad_request(req);
210 return 0;
211 }
212
213 uri_len = strlen(req->request_uri);
214
215 current = find_alias(req->request_uri, uri_len);
216 if (current) {
217
218 if (current->type == SCRIPTALIAS) /* Script */
219 return init_script_alias(req, current, uri_len);
220
221 /* not a script alias, therefore begin filling in data */
222
223 {
224 int len;
225 len = current->real_len;
226 len += uri_len - current->fake_len;
227 if (len > MAX_HEADER_LENGTH) {
228 log_error_doc(req);
229 fputs("uri too long!\n", stderr);
230 send_r_bad_request(req);
231 return 0;
232 }
233 memcpy(buffer, current->realname, current->real_len);
234 memcpy(buffer + current->real_len,
235 req->request_uri + current->fake_len,
236 uri_len - current->fake_len + 1);
237 }
238
239 if (current->type == REDIRECT) { /* Redirect */
240 if (req->method == M_POST) { /* POST to non-script */
241 /* it's not a cgi, but we try to POST??? */
242 send_r_bad_request(req);
243 return 0; /* not a script alias, therefore begin filling in data */
244 }
245 send_r_moved_temp(req, buffer, "");
246 return 0;
247 } else { /* Alias */
248 req->pathname = strdup(buffer);
249 if (!req->pathname) {
250 send_r_error(req);
251 WARN("unable to strdup buffer onto req->pathname");
252 return 0;
253 }
254 return 1;
255 }
256 }
257
258 /*
259 The reason why this is *not* an 'else if' is that,
260 after aliasing, we still have to check for '~' expansion
261 */
262
263 if (user_dir && req->request_uri[1] == '~') {
264 char *user_homedir;
265
266 req_urip = req->request_uri + 2;
267
268 /* since we have uri_len which is from strlen(req->request_uri) */
269 p = memchr(req_urip, '/', uri_len - 2);
270 if (p)
271 *p = '\0';
272
273 user_homedir = get_home_dir(req_urip);
274 if (p) /* have to restore request_uri in case of error */
275 *p = '/';
276
277 if (!user_homedir) { /*no such user */
278 send_r_not_found(req);
279 return 0;
280 }
281 {
282 int l1 = strlen(user_homedir);
283 int l2 = strlen(user_dir);
284 int l3 = (p ? strlen(p) : 0);
285
286 if (l1 + l2 + l3 + 1 > 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
293 memcpy(buffer, user_homedir, l1);
294 buffer[l1] = '/';
295 memcpy(buffer + l1 + 1, user_dir, l2 + 1);
296 if (p)
297 memcpy(buffer + l1 + 1 + l2, p, l3 + 1);
298 }
299 } else if (document_root) {
300 /* no aliasing, no userdir... */
301 int l1, l2, l3;
302
303 l1 = strlen(document_root);
304 l2 = strlen(req->request_uri);
305 if (virtualhost)
306 l3 = strlen(req->local_ip_addr);
307 else
308 l3 = 0;
309
310 if (l1 + l2 + l3 + 1 > MAX_HEADER_LENGTH) {
311 log_error_doc(req);
312 fputs("uri too long!\n", stderr);
313 send_r_bad_request(req);
314 return 0;
315 }
316
317 /* the 'l2 + 1' is there so we copy the '\0' as well */
318 memcpy(buffer, document_root, l1);
319 if (virtualhost) {
320 buffer[l1] = '/';
321 memcpy(buffer + l1 + 1, req->local_ip_addr, l3);
322 memcpy(buffer + l1 + 1 + l3, req->request_uri, l2 + 1);
323 } else
324 memcpy(buffer + l1, req->request_uri, l2 + 1);
325 } else {
326 /* not aliased. not userdir. not part of document_root. BAIL */
327 send_r_not_found(req);
328 return 0;
329 }
330
331 /* if here,
332 * o it may be aliased but it's not a redirect or a script...
333 * o it may be a homedir
334 * o it may be a document_root resource (with or without virtual host)
335 */
336
337 req->pathname = strdup(buffer);
338 if (!req->pathname) {
339 WARN("Could not strdup buffer for req->pathname!");
340 send_r_error(req);
341 return 0;
342 }
343
344 /* below we support cgis outside of a ScriptAlias */
345 if (strcmp(CGI_MIME_TYPE, get_mime_type(buffer)) == 0) { /* cgi */
346 #ifdef FASCIST_LOGGING
347 log_error_time();
348 fprintf(stderr, "%s:%d - buffer is: \"%s\"\n",
349 __FILE__, __LINE__, buffer);
350 #endif
351 /* FIXME */
352 /* script_name could end up as /cgi-bin/bob/extra_path */
353 req->script_name = strdup(req->request_uri);
354 if (!req->script_name) {
355 WARN("Could not strdup req->request_uri for req->script_name");
356 send_r_error(req);
357 return 0;
358 }
359 if (req->simple)
360 req->is_cgi = NPH;
361 else
362 req->is_cgi = CGI;
363 return 1;
364 } else if (req->method == M_POST) { /* POST to non-script */
365 /* it's not a cgi, but we try to POST??? */
366 send_r_bad_request(req);
367 return 0;
368 } else /* we are done!! */
369 return 1;
370 }
371
372 /*
373 * Name: init_script_alias
374 *
375 * Description: Performs full parsing on a ScriptAlias request
376 * Sets path_info and script_name
377 *
378 * Return values:
379 *
380 * 0: failure, shut down
381 * 1: success, continue
382 */
383
384 int init_script_alias(request * req, alias * current1, int uri_len)
385 {
386 static char pathname[MAX_HEADER_LENGTH + 1];
387 struct stat statbuf;
388 static char buffer[MAX_HEADER_LENGTH + 1];
389
390 int index = 0;
391 char c;
392 int err;
393
394 /* copies the "real" path + the non-alias portion of the
395 uri to pathname.
396 */
397
398 if (uri_len - current1->fake_len + current1->real_len >
399 MAX_HEADER_LENGTH) {
400 log_error_doc(req);
401 fputs("uri too long!\n", stderr);
402 send_r_bad_request(req);
403 return 0;
404 }
405
406 memcpy(pathname, current1->realname, current1->real_len);
407 memcpy(pathname + current1->real_len,
408 &req->request_uri[current1->fake_len],
409 uri_len - current1->fake_len + 1); /* the +1 copies the NUL */
410 #ifdef FASCIST_LOGGING
411 log_error_time();
412 fprintf(stderr,
413 "%s:%d - pathname in init_script_alias is: \"%s\" (\"%s\")\n",
414 __FILE__, __LINE__, pathname, pathname + current1->real_len);
415 #endif
416 if (strncmp("nph-", pathname + current1->real_len, 4) == 0
417 || req->simple) req->is_cgi = NPH;
418 else
419 req->is_cgi = CGI;
420
421
422 /* start at the beginning of the actual uri...
423 (in /cgi-bin/bob, start at the 'b' in bob */
424 index = current1->real_len;
425
426 /* go to first and successive '/' and keep checking
427 * if it is a full pathname
428 * on success (stat (not lstat) of file is a *regular file*)
429 */
430 do {
431 c = pathname[++index];
432 if (c == '/') {
433 pathname[index] = '\0';
434 err = stat(pathname, &statbuf);
435 pathname[index] = '/';
436 if (err == -1) {
437 send_r_not_found(req);
438 return 0;
439 }
440
441 /* is it a dir? */
442 if (!S_ISDIR(statbuf.st_mode)) {
443 /* check access */
444 if (!(statbuf.st_mode &
445 (S_IFREG | /* regular file */
446 (S_IRUSR | S_IXUSR) | /* u+rx */
447 (S_IRGRP | S_IXGRP) | /* g+rx */
448 (S_IROTH | S_IXOTH)))) { /* o+rx */
449 send_r_forbidden(req);
450 return 0;
451 }
452 /* stop here */
453 break;
454 }
455 }
456 } while (c != '\0');
457
458 req->script_name = strdup(req->request_uri);
459 if (!req->script_name) {
460 send_r_error(req);
461 WARN("unable to strdup req->request_uri for req->script_name");
462 return 0;
463 }
464
465 if (c == '\0') {
466 err = stat(pathname, &statbuf);
467 if (err == -1) {
468 send_r_not_found(req);
469 return 0;
470 }
471
472 /* is it a dir? */
473 if (!S_ISDIR(statbuf.st_mode)) {
474 /* check access */
475 if (!(statbuf.st_mode &
476 (S_IFREG | /* regular file */
477 (S_IRUSR | S_IXUSR) | /* u+rx */
478 (S_IRGRP | S_IXGRP) | /* g+rx */
479 (S_IROTH | S_IXOTH)))) { /* o+rx */
480 send_r_forbidden(req);
481 return 0;
482 }
483 /* stop here */
484 } else {
485 send_r_forbidden(req);
486 return 0;
487 }
488 }
489
490 /* we have path_info if c == '/'... still have to check for query */
491 else if (c == '/') {
492 int hash;
493 alias *current;
494 int path_len;
495
496 req->path_info = strdup(pathname + index);
497 if (!req->path_info) {
498 send_r_error(req);
499 WARN("unable to strdup pathname + index for req->path_info");
500 return 0;
501 }
502 pathname[index] = '\0'; /* strip path_info from path */
503 path_len = strlen(req->path_info);
504 /* we need to fix script_name here */
505 /* index points into pathname, which is
506 * realname/cginame/foo
507 * and index points to the '/foo' part
508 */
509 req->script_name[strlen(req->script_name) - path_len] = '\0'; /* zap off the /foo part */
510
511 /* now, we have to re-alias the extra path info....
512 this sucks.
513 */
514 hash = get_alias_hash_value(req->path_info);
515 current = alias_hashtable[hash];
516 while (current && !req->path_translated) {
517 if (!strncmp(req->path_info, current->fakename,
518 current->fake_len)) {
519
520 if (current->real_len +
521 path_len - current->fake_len > MAX_HEADER_LENGTH) {
522 log_error_doc(req);
523 fputs("uri too long!\n", stderr);
524 send_r_bad_request(req);
525 return 0;
526 }
527
528 memcpy(buffer, current->realname, current->real_len);
529 strcpy(buffer + current->real_len,
530 &req->path_info[current->fake_len]);
531 req->path_translated = strdup(buffer);
532 if (!req->path_translated) {
533 send_r_error(req);
534 WARN("unable to strdup buffer for req->path_translated");
535 return 0;
536 }
537 }
538 current = current->next;
539 }
540 /* no alias... try userdir */
541 if (!req->path_translated && user_dir && req->path_info[1] == '~') {
542 char *user_homedir;
543 char *p;
544
545 p = strchr(pathname + index + 1, '/');
546 if (p)
547 *p = '\0';
548
549 user_homedir = get_home_dir(pathname + index + 2);
550 if (p)
551 *p = '/';
552
553 if (!user_homedir) { /* no such user */
554 send_r_not_found(req);
555 return 0;
556 }
557 {
558 int l1 = strlen(user_homedir);
559 int l2 = strlen(user_dir);
560 int l3;
561 if (p)
562 l3 = strlen(p);
563 else
564 l3 = 0;
565
566 req->path_translated = malloc(l1 + l2 + l3 + 2);
567 if (req->path_translated == NULL) {
568 send_r_error(req);
569 WARN("unable to malloc memory for req->path_translated");
570 return 0;
571 }
572 memcpy(req->path_translated, user_homedir, l1);
573 req->path_translated[l1] = '/';
574 memcpy(req->path_translated + l1 + 1, user_dir, l2 + 1);
575 if (p)
576 memcpy(req->path_translated + l1 + 1 + l2, p, l3 + 1);
577 }
578 }
579 if (!req->path_translated && document_root) {
580 /* no userdir, no aliasing... try document root */
581 int l1, l2;
582 l1 = strlen(document_root);
583 l2 = path_len;
584
585 req->path_translated = malloc(l1 + l2 + 1);
586 if (req->path_translated == NULL) {
587 send_r_error(req);
588 WARN("unable to malloc memory for req->path_translated");
589 return 0;
590 }
591 memcpy(req->path_translated, document_root, l1);
592 memcpy(req->path_translated + l1, req->path_info, l2 + 1);
593 }
594 }
595
596 req->pathname = strdup(pathname);
597 if (!req->pathname) {
598 send_r_error(req);
599 WARN("unable to strdup pathname for req->pathname");
600 return 0;
601 }
602
603 return 1;
604 }
605
606 /*
607 * Empties the alias hashtable, deallocating any allocated memory.
608 */
609
610 void dump_alias(void)
611 {
612 int i;
613 alias *temp;
614
615 for (i = 0; i < ALIAS_HASHTABLE_SIZE; ++i) { /* these limits OK? */
616 if (alias_hashtable[i]) {
617 temp = alias_hashtable[i];
618 while (temp) {
619 alias *temp_next;
620
621 if (temp->fakename)
622 free(temp->fakename);
623 if (temp->realname)
624 free(temp->realname);
625 temp_next = temp->next;
626 free(temp);
627 temp = temp_next;
628 }
629 alias_hashtable[i] = NULL;
630 }
631 }
632 }

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26