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

Annotation of /hydra/src/cgi.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.29 - (hide annotations)
Thu Mar 9 18:11:07 2006 UTC (18 years ago) by nmav
Branch: MAIN
CVS Tags: hydra_0_1_6_without_hic, hydra_0_1_8, HEAD
Changes since 1.28: +3 -34 lines
File MIME type: text/plain
Removed the HIC support.

1 nmav 1.1 /*
2 nmav 1.14 * Hydra, an http server
3 nmav 1.1 * Copyright (C) 1995 Paul Phillips <paulp@go2net.com>
4     * Some changes Copyright (C) 1996,97 Larry Doolittle <ldoolitt@boa.org>
5     * Some changes Copyright (C) 1996 Charles F. Randall <crandall@goldsys.com>
6     * Some changes Copyright (C) 1997-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 nmav 1.29 /* $Id: cgi.c,v 1.28 2004/07/24 17:35:37 nmav Exp $ */
25 nmav 1.1
26     #include "boa.h"
27    
28     static char *env_gen_extra(const char *key, const char *value, int extra);
29    
30     int verbose_cgi_logs = 0;
31     /* The +1 is for the the NULL in complete_env */
32     static char *common_cgi_env[COMMON_CGI_COUNT + 1];
33    
34     /*
35     * Name: create_common_env
36     *
37     * Description: Set up the environment variables that are common to
38     * all CGI scripts
39     */
40    
41     void create_common_env()
42     {
43 nmav 1.27 int ix = 0, i;
44 nmav 1.1
45    
46 nmav 1.7 /* NOTE NOTE NOTE:
47     If you (the reader) someday modify this chunk of code to
48     handle more "common" CGI environment variables, then bump the
49     value COMMON_CGI_COUNT in defines.h UP
50    
51     Also, in the case of document_root and server_admin, two variables
52     that may or may not be defined depending on the way the server
53     is configured, we check for null values and use an empty
54     string to denote a NULL value to the environment, as per the
55     specification. The quote for which follows:
56    
57     "In all cases, a missing environment variable is
58     equivalent to a zero-length (NULL) value, and vice versa."
59     */
60 nmav 1.27 common_cgi_env[ix++] = env_gen_extra("PATH",
61 nmav 1.7 ((cgi_path !=
62     NULL) ? cgi_path :
63     DEFAULT_PATH), 0);
64 nmav 1.27 common_cgi_env[ix++] =
65 nmav 1.7 env_gen_extra("SERVER_SOFTWARE", SERVER_NAME "/" SERVER_VERSION, 0);
66 nmav 1.27 common_cgi_env[ix++] =
67 nmav 1.7 env_gen_extra("GATEWAY_INTERFACE", CGI_VERSION, 0);
68    
69     /* removed the SERVER_PORT which may change due to SSL support
70     * Also removed the DOCUMENT_ROOT, SERVER_NAME, which are now per request.
71     */
72    
73    
74 nmav 1.22 /* NCSA added */
75     #ifdef USE_NCSA_CGI_ENV
76 nmav 1.27 common_cgi_env[ix++] = env_gen_extra("SERVER_ROOT", server_root);
77 nmav 1.22 #endif
78 nmav 1.7
79     /* APACHE added */
80 nmav 1.27 common_cgi_env[ix++] =
81 nmav 1.7 env_gen_extra("SERVER_ADMIN", server_admin, 0);
82 nmav 1.27 common_cgi_env[ix] = NULL;
83 nmav 1.7
84     /* Sanity checking -- make *sure* the memory got allocated */
85 nmav 1.27 if (ix > COMMON_CGI_COUNT) {
86 nmav 1.7 log_error_time();
87     fprintf(stderr, "COMMON_CGI_COUNT not high enough.\n");
88     exit(1);
89     }
90    
91 nmav 1.27 for (i = 0; i < ix; ++i) {
92 nmav 1.7 if (common_cgi_env[i] == NULL) {
93     log_error_time();
94     fprintf(stderr,
95     "Unable to allocate a component of common_cgi_env - out of memory.\n");
96     exit(1);
97     }
98     }
99 nmav 1.1 }
100    
101     void clear_common_env(void)
102     {
103 nmav 1.7 int i;
104 nmav 1.1
105 nmav 1.7 for (i = 0; i <= COMMON_CGI_COUNT; ++i) {
106     if (common_cgi_env[i] != NULL) {
107     free(common_cgi_env[i]);
108     common_cgi_env[i] = NULL;
109     }
110     }
111 nmav 1.1 }
112    
113     /*
114     * Name: env_gen_extra
115     * (and via a not-so-tricky #define, env_gen)
116     * This routine calls malloc: please free the memory when you are done
117     */
118     static char *env_gen_extra(const char *key, const char *value, int extra)
119     {
120 nmav 1.7 char *result;
121     int key_len, value_len;
122 nmav 1.1
123 nmav 1.7 if (value == NULL) /* ServerAdmin may not be defined, eg */
124     value = "";
125     key_len = strlen(key);
126     value_len = strlen(value);
127     /* leave room for '=' sign and null terminator */
128     result = malloc(extra + key_len + value_len + 2);
129     if (result) {
130     memcpy(result + extra, key, key_len);
131     *(result + extra + key_len) = '=';
132     memcpy(result + extra + key_len + 1, value, value_len);
133     *(result + extra + key_len + value_len + 1) = '\0';
134     } else {
135     log_error_time();
136     perror("malloc");
137     log_error_time();
138     fprintf(stderr,
139     "tried to allocate (key=value) extra=%d: %s=%s\n",
140     extra, key, value);
141     }
142     return result;
143 nmav 1.1 }
144    
145     /*
146     * Name: add_cgi_env
147     *
148     * Description: adds a variable to CGI's environment
149     * Used for HTTP_ headers
150     */
151    
152 nmav 1.10 int add_cgi_env(request * req, const char *key, const char *value,
153     int http_prefix)
154 nmav 1.1 {
155 nmav 1.7 char *p;
156     int prefix_len;
157 nmav 1.1
158 nmav 1.7 if (http_prefix) {
159     prefix_len = 5;
160     } else {
161     prefix_len = 0;
162     }
163    
164     if (req->cgi_env_index < CGI_ENV_MAX) {
165     p = env_gen_extra(key, value, prefix_len);
166     if (!p) {
167 nmav 1.27 log_error_doc(req);
168 nmav 1.7 fprintf(stderr, "Unable to generate additional CGI Environment"
169     "variable -- ran out of memory!\n");
170 nmav 1.27 return 0;
171 nmav 1.7 }
172     if (prefix_len)
173     memcpy(p, "HTTP_", 5);
174     req->cgi_env[req->cgi_env_index++] = p;
175     return 1;
176     } else {
177 nmav 1.27 log_error_doc(req);
178 nmav 1.7 fprintf(stderr, "Unable to generate additional CGI Environment"
179     "variable -- not enough space!\n");
180     }
181     return 0;
182 nmav 1.1 }
183    
184     #define my_add_cgi_env(req, key, value) { \
185     int ok = add_cgi_env(req, key, value, 0); \
186     if (!ok) return 0; \
187     }
188    
189 nmav 1.10 const char *hydra_method_str(int method)
190 nmav 1.1 {
191 nmav 1.10 char *w;
192     switch (method) {
193     case M_POST:
194     w = "POST";
195     break;
196     case M_HEAD:
197     w = "HEAD";
198     break;
199     case M_GET:
200     w = "GET";
201     break;
202     default:
203     w = "UNKNOWN";
204     break;
205     }
206     return w;
207 nmav 1.8 }
208    
209     /*
210     * Name: complete_env
211     *
212     * Description: adds the known client header env variables
213     * and terminates the environment array
214     */
215    
216     int complete_env(request * req)
217     {
218     int i;
219     char buf[22];
220 nmav 1.10 const char *w;
221 nmav 1.8
222     for (i = 0; common_cgi_env[i]; i++)
223     req->cgi_env[i] = common_cgi_env[i];
224    
225 nmav 1.10 w = hydra_method_str(req->method);
226 nmav 1.8 my_add_cgi_env(req, "REQUEST_METHOD", w);
227 nmav 1.7
228 nmav 1.28 if (req->action)
229     my_add_cgi_env(req, "REDIRECT_STATUS", "200");
230    
231 nmav 1.7 if (req->secure) {
232     simple_itoa(ssl_port, buf);
233     } else {
234     simple_itoa(server_port, buf);
235     }
236     my_add_cgi_env(req, "SERVER_PORT", buf);
237     my_add_cgi_env(req, "SERVER_NAME", req->hostname);
238    
239     /* NCSA and APACHE added -- not in CGI spec */
240 nmav 1.22 #ifdef USE_NCSA_CGI_ENV
241     my_add_cgi_env( req, "DOCUMENT_ROOT", req->document_root);
242     #endif
243 nmav 1.7
244     my_add_cgi_env(req, "SERVER_ADDR", req->local_ip_addr);
245 nmav 1.25 my_add_cgi_env(req, "SERVER_PROTOCOL", req->http_version_str);
246 nmav 1.7 my_add_cgi_env(req, "REQUEST_URI", req->request_uri);
247    
248     if (req->path_info)
249     my_add_cgi_env(req, "PATH_INFO", req->path_info);
250    
251     if (req->path_translated)
252     /* while path_translated depends on path_info,
253     * there are cases when path_translated might
254     * not exist when path_info does
255     */
256     my_add_cgi_env(req, "PATH_TRANSLATED", req->path_translated);
257    
258     my_add_cgi_env(req, "SCRIPT_NAME", req->script_name);
259    
260     if (req->query_string)
261     my_add_cgi_env(req, "QUERY_STRING", req->query_string);
262     my_add_cgi_env(req, "REMOTE_ADDR", req->remote_ip_addr);
263    
264     simple_itoa(req->remote_port, buf);
265     my_add_cgi_env(req, "REMOTE_PORT", buf);
266    
267     if (req->method == M_POST) {
268     if (req->content_type) {
269     my_add_cgi_env(req, "CONTENT_TYPE", req->content_type);
270     } else {
271     my_add_cgi_env(req, "CONTENT_TYPE", default_type);
272     }
273     if (req->content_length) {
274     my_add_cgi_env(req, "CONTENT_LENGTH", req->content_length);
275     }
276     }
277 nmav 1.1 #ifdef ACCEPT_ON
278 nmav 1.7 if (req->accept[0])
279     my_add_cgi_env(req, "HTTP_ACCEPT", req->accept);
280 nmav 1.1 #endif
281    
282 nmav 1.7 if (req->cgi_env_index < CGI_ENV_MAX + 1) {
283     req->cgi_env[req->cgi_env_index] = NULL; /* terminate */
284     return 1;
285     }
286     log_error_time();
287     fprintf(stderr, "Not enough space in CGI environment for remainder"
288     " of variables.\n");
289     return 0;
290 nmav 1.1 }
291    
292     /*
293     * Name: make_args_cgi
294     *
295     * Build argv list for a CGI script according to spec
296     *
297     */
298    
299     void create_argv(request * req, char **aargv)
300     {
301 nmav 1.7 char *p, *q, *r;
302     int aargc;
303 nmav 1.1
304 nmav 1.7 q = req->query_string;
305     aargv[0] = req->pathname;
306 nmav 1.1
307 nmav 1.7 /* here, we handle a special "indexed" query string.
308     * Taken from the CGI/1.1 SPEC:
309     * This is identified by a GET or HEAD request with a query string
310     * with no *unencoded* '=' in it.
311     * For such a request, I'm supposed to parse the search string
312     * into words, according to the following rules:
313    
314     search-string = search-word *( "+" search-word )
315     search-word = 1*schar
316     schar = xunreserved | escaped | xreserved
317     xunreserved = alpha | digit | xsafe | extra
318     xsafe = "$" | "-" | "_" | "."
319     xreserved = ";" | "/" | "?" | ":" | "@" | "&"
320    
321     After parsing, each word is URL-decoded, optionally encoded in a system
322     defined manner, and then the argument list
323     is set to the list of words.
324    
325    
326     Thus, schar is alpha|digit|"$"|"-"|"_"|"."|";"|"/"|"?"|":"|"@"|"&"
327    
328     As of this writing, escape.pl escapes the following chars:
329    
330     "-", "_", ".", "!", "~", "*", "'", "(", ")",
331     "0".."9", "A".."Z", "a".."z",
332     ";", "/", "?", ":", "@", "&", "=", "+", "\$", ","
333    
334     Which therefore means
335     "=", "+", "~", "!", "*", "'", "(", ")", ","
336     are *not* escaped and should be?
337     Wait, we don't do any escaping, and nor should we.
338     According to the RFC draft, we unescape and then re-escape
339     in a "system defined manner" (here: none).
340    
341     The CGI/1.1 draft (03, latest is 1999???) is very unclear here.
342    
343     I am using the latest published RFC, 2396, for what does and does
344     not need escaping.
345    
346     Since boa builds the argument list and does not call /bin/sh,
347     (boa uses execve for CGI)
348     */
349    
350     if (q && !strchr(q, '=')) {
351     /* we have an 'index' style */
352     q = strdup(q);
353     if (!q) {
354     WARN("unable to strdup 'q' in create_argv!");
355     }
356     for (aargc = 1; q && (aargc < CGI_ARGC_MAX);) {
357     r = q;
358     /* for an index-style CGI, + is used to seperate arguments
359     * an escaped '+' is of no concern to us
360     */
361     if ((p = strchr(q, '+'))) {
362     *p = '\0';
363     q = p + 1;
364     } else {
365     q = NULL;
366     }
367     if (unescape_uri(r, NULL)) {
368     /* printf("parameter %d: %s\n",aargc,r); */
369     aargv[aargc++] = r;
370     }
371     }
372     aargv[aargc] = NULL;
373     } else {
374     aargv[1] = NULL;
375     }
376 nmav 1.1 }
377    
378 nmav 1.25
379 nmav 1.1 /*
380     * Name: init_cgi
381     *
382     * Description: Called for GET/POST requests that refer to ScriptAlias
383 nmav 1.7 * directories or application/x-httpd-cgi files. Pipes are used for the
384     * communication with the child.
385 nmav 1.1 * stderr remains tied to our log file; is this good?
386     *
387     * Returns:
388     * 0 - error or NPH, either way the socket is closed
389     * 1 - success
390     */
391    
392     int init_cgi(request * req)
393     {
394 nmav 1.7 int child_pid;
395     int pipes[2];
396    
397     SQUASH_KA(req);
398    
399 nmav 1.29 if (req->is_cgi == NPH || req->is_cgi == CGI
400 nmav 1.25 || req->is_cgi == CGI_ACTION)
401     {
402 nmav 1.20 if (req->secure && complete_env_ssl(req) == 0) {
403     return 0;
404     }
405 nmav 1.7 if (complete_env(req) == 0) {
406     return 0;
407     }
408     }
409 nmav 1.1 #ifdef FASCIST_LOGGING
410 nmav 1.7 {
411     int i;
412     for (i = 0; i < req->cgi_env_index; ++i)
413     fprintf(stderr, "%s - environment variable for cgi: \"%s\"\n",
414     __FILE__, req->cgi_env[i]);
415     }
416 nmav 1.1 #endif
417    
418 nmav 1.7 if (req->is_cgi) {
419     if (pipe(pipes) == -1) {
420     log_error_time();
421     perror("pipe");
422     return 0;
423     }
424    
425     /* set the read end of the socket to non-blocking */
426     if (set_nonblock_fd(pipes[0]) == -1) {
427     log_error_time();
428     perror("cgi-fcntl");
429     close(pipes[0]);
430     close(pipes[1]);
431     return 0;
432     }
433     } else {
434     log_error_time();
435     fprintf(stderr, "Non CGI in init_cgi()!\n");
436     return 0;
437     }
438    
439 nmav 1.10 child_pid = fork();
440     switch (child_pid) {
441     case -1:
442     /* fork unsuccessful */
443     log_error_time();
444     perror("fork");
445 nmav 1.7
446 nmav 1.10 close(pipes[0]);
447     close(pipes[1]);
448 nmav 1.7
449 nmav 1.10 send_r_error(req);
450     /* FIXME: There is aproblem here. send_r_error would work
451     for NPH and CGI, but not for GUNZIP. Fix that. */
452     /* i'd like to send_r_error, but.... */
453     return 0;
454     break;
455     case 0:
456     /* child */
457 nmav 1.25 if (req->is_cgi == CGI || req->is_cgi == NPH || req->is_cgi == CGI_ACTION)
458     {
459 nmav 1.22 int l;
460     char *newpath;
461 nmav 1.10 char *c;
462    
463 nmav 1.22 c = strrchr(req->pathname, '/');
464     if (!c) {
465     /* there will always be a '.' */
466     log_error_time();
467     WARN("unable to find '/' in req->pathname");
468     close(pipes[1]);
469 nmav 1.10 _exit(1);
470     }
471 nmav 1.22
472     *c = '\0';
473    
474     if (chdir(req->pathname) != 0) {
475 nmav 1.10 log_error_time();
476     perror("chdir");
477     close(pipes[1]);
478     _exit(1);
479     }
480 nmav 1.22
481     req->pathname = ++c;
482     l = strlen(req->pathname) + 3;
483     /* prefix './' */
484     newpath = malloc(sizeof(char) * l);
485     if (!newpath) {
486     /* there will always be a '.' */
487 nmav 1.10 log_error_time();
488 nmav 1.22 perror("unable to malloc for newpath");
489 nmav 1.10 close(pipes[1]);
490     _exit(1);
491     }
492 nmav 1.22 newpath[0] = '.';
493     newpath[1] = '/';
494     memcpy(&newpath[2], req->pathname, l - 2); /* includes the trailing '\0' */
495     req->pathname = newpath;
496 nmav 1.7 }
497 nmav 1.22
498     /* close the 'read' end of the pipes[] */
499     close(pipes[0]);
500    
501     /* tie cgi's STDOUT to our write end of pipe */
502     if (dup2(pipes[1], STDOUT_FILENO) == -1) {
503     log_error_time();
504     perror("dup2 - pipes");
505     _exit(1);
506     }
507     close(pipes[1]);
508    
509 nmav 1.10 /* tie post_data_fd to POST stdin */
510     if (req->method == M_POST) { /* tie stdin to file */
511 nmav 1.23 if (req->post_data_fd.pipe==0) {
512 nmav 1.26 if (lseek(req->post_data_fd.fds[0], SEEK_SET, 0) == (off_t)-1) {
513     log_error_time();
514     perror("lseek");
515     _exit(1);
516     }
517 nmav 1.23 }
518    
519     dup2(req->post_data_fd.fds[0], STDIN_FILENO);
520     close_tmp_fd( &req->post_data_fd);
521 nmav 1.10 }
522 nmav 1.22
523     umask(cgi_umask); /* change umask *again* u=rwx,g=rxw,o= */
524 nmav 1.10
525     /*
526     * tie STDERR to cgi_log_fd
527     * cgi_log_fd will automatically close, close-on-exec rocks!
528     * if we don't tied STDERR (current log_error) to cgi_log_fd,
529     * then we ought to close it.
530     */
531 nmav 1.22 if (cgi_log_fd) {
532     dup2(cgi_log_fd, STDERR_FILENO);
533     close( cgi_log_fd);
534     }
535 nmav 1.10
536     if (req->is_cgi == NPH || req->is_cgi == CGI) {
537     char *aargv[CGI_ARGC_MAX + 1];
538 nmav 1.19 create_argv(req, aargv);
539 nmav 1.10 execve(req->pathname, aargv, req->cgi_env);
540 nmav 1.25 } else if (req->is_cgi == CGI_ACTION) {
541     char *aargv[CGI_ARGC_MAX + 2];
542     aargv[0] = req->action;
543     create_argv(req, &aargv[1]);
544     execve(req->action, aargv, req->cgi_env);
545 nmav 1.10 } else {
546     if (req->is_cgi == INDEXER_CGI)
547     execl(dirmaker, dirmaker, req->pathname, req->request_uri,
548     NULL);
549     }
550     /* execve failed */
551     WARN(req->pathname);
552 nmav 1.7 _exit(1);
553    
554 nmav 1.10 break; /* it doesn't matter, we never make it until here */
555    
556     default:
557     /* parent */
558     /* if here, fork was successful */
559     if (verbose_cgi_logs) {
560     log_error_time();
561     fprintf(stderr, "Forked child \"%s\" pid %d\n",
562     req->pathname, child_pid);
563     }
564 nmav 1.7
565 nmav 1.10 if (req->method == M_POST) {
566 nmav 1.23 close_tmp_fd( &req->post_data_fd);
567 nmav 1.10 }
568 nmav 1.7
569 nmav 1.10 /* NPH, etc... all go straight to the fd */
570 nmav 1.7
571 nmav 1.10 close(pipes[1]);
572     break;
573 nmav 1.7 }
574    
575 nmav 1.10 /* we only get here in parent case, and
576     * success.
577     */
578 nmav 1.7
579 nmav 1.10 req->data_fd = pipes[0];
580 nmav 1.7
581 nmav 1.10 req->status = PIPE_READ;
582 nmav 1.25 if (req->is_cgi == CGI || req->is_cgi == HIC_CGI || req->is_cgi == CGI_ACTION)
583     {
584 nmav 1.10 req->cgi_status = CGI_PARSE; /* got to parse cgi header */
585     /* for cgi_header... I get half the buffer! */
586     req->header_line = req->header_end = (req->buffer + BUFFER_SIZE / 2);
587 nmav 1.29 } else { /* NPH CGIs */
588 nmav 1.10 req->cgi_status = CGI_BUFFER;
589     /* I get all the buffer! */
590     req->header_line = req->header_end = req->buffer;
591     }
592 nmav 1.7
593 nmav 1.10 /* reset req->filepos for logging (it's used in pipe.c) */
594     /* still don't know why req->filesize might be reset though */
595     req->filepos = 0;
596 nmav 1.1
597 nmav 1.7 return 1;
598 nmav 1.1 }

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26