source: rtems/cpukit/shttpd/cgi.c @ 086442b7

4.104.115
Last change on this file since 086442b7 was 086442b7, checked in by Ralf Corsepius <ralf.corsepius@…>, on 12/02/09 at 11:00:38

Whitespace removal.

  • Property mode set to 100644
File size: 7.5 KB
Line 
1/*
2 * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
3 * All rights reserved
4 *
5 * "THE BEER-WARE LICENSE" (Revision 42):
6 * Sergey Lyubka wrote this file.  As long as you retain this notice you
7 * can do whatever you want with this stuff. If we meet some day, and you think
8 * this stuff is worth it, you can buy me a beer in return.
9 */
10
11#include "defs.h"
12
13#if !defined(NO_CGI)
14struct env_block {
15        char    buf[ENV_MAX];           /* Environment buffer           */
16        int     len;                    /* Space taken                  */
17        char    *vars[CGI_ENV_VARS];    /* Point into the buffer        */
18        int     nvars;                  /* Number of variables          */
19};
20
21/*
22 * Verify that given file has CGI extension
23 */
24int
25is_cgi(struct shttpd_ctx *ctx, const char *path)
26{
27        size_t          len, path_len;
28        const char      *s = ctx->cgi_extensions;
29       
30        path_len = strlen(path);
31
32        FOR_EACH_WORD_IN_LIST(s, len)
33                if (len < path_len &&
34                    !my_strncasecmp(path + path_len - len, s, len))
35                        return (1);
36
37        return (0);
38}
39
40/*
41 * UNIX socketpair() implementation. Why? Because Windows does not have it.
42 * Return 0 on success, -1 on error.
43 */
44static int
45my_socketpair(struct conn *c, int sp[2])
46{
47        struct sockaddr_in      sa;
48        int                     sock, ret = -1;
49        socklen_t               len = sizeof(sa);
50
51        (void) memset(&sa, 0, sizeof(sa));
52        sa.sin_family           = AF_INET;
53        sa.sin_port             = htons(0);
54        sa.sin_addr.s_addr      = htonl(INADDR_LOOPBACK);
55
56        if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
57                elog(E_LOG, c, "mysocketpair: socket(): %d", ERRNO);
58        } else if (bind(sock, (struct sockaddr *) &sa, len) != 0) {
59                elog(E_LOG, c, "mysocketpair: bind(): %d", ERRNO);
60                (void) closesocket(sock);
61        } else if (listen(sock, 1) != 0) {
62                elog(E_LOG, c, "mysocketpair: listen(): %d", ERRNO);
63                (void) closesocket(sock);
64        } else if (getsockname(sock, (struct sockaddr *) &sa, &len) != 0) {
65                elog(E_LOG, c, "mysocketpair: getsockname(): %d", ERRNO);
66                (void) closesocket(sock);
67        } else if ((sp[0] = socket(AF_INET, SOCK_STREAM, 6)) == -1) {
68                elog(E_LOG, c, "mysocketpair: socket(): %d", ERRNO);
69                (void) closesocket(sock);
70        } else if (connect(sp[0], (struct sockaddr *) &sa, len) != 0) {
71                elog(E_LOG, c, "mysocketpair: connect(): %d", ERRNO);
72                (void) closesocket(sock);
73                (void) closesocket(sp[0]);
74        } else if ((sp[1] = accept(sock,(struct sockaddr *) &sa, &len)) == -1) {
75                elog(E_LOG, c, "mysocketpair: accept(): %d", ERRNO);
76                (void) closesocket(sock);
77                (void) closesocket(sp[0]);
78        } else {
79                /* Success */
80                ret = 0;
81                (void) closesocket(sock);
82        }
83
84#ifndef _WIN32
85        (void) fcntl(sp[0], F_SETFD, FD_CLOEXEC);
86        (void) fcntl(sp[1], F_SETFD, FD_CLOEXEC);
87#endif /* _WIN32*/
88
89        return (ret);
90}
91
92static void
93addenv(struct env_block *block, const char *fmt, ...)
94{
95        int     n, space;
96        va_list ap;
97
98        space = sizeof(block->buf) - block->len - 2;
99        assert(space >= 0);
100
101        va_start(ap, fmt);
102        n = vsnprintf(block->buf + block->len, space, fmt, ap);
103        va_end(ap);
104
105        if (n > 0 && n < space && block->nvars < CGI_ENV_VARS - 2) {
106                block->vars[block->nvars++] = block->buf + block->len;
107                block->len += n + 1;    /* Include \0 terminator */
108        }
109}
110
111static void
112add_http_headers_to_env(struct env_block *b, const char *s, int len)
113{
114        const char      *p, *v, *e = s + len;
115        int             space, n, i, ch;
116
117        /* Loop through all headers in the request */
118        while (s < e) {
119
120                /* Find where this header ends. Remember where value starts */
121                for (p = s, v = NULL; p < e && *p != '\n'; p++)
122                        if (v == NULL && *p == ':')
123                                v = p;
124
125                /* 2 null terminators and "HTTP_" */
126                space = (sizeof(b->buf) - b->len) - (2 + 5);
127                assert(space >= 0);
128       
129                /* Copy header if enough space in the environment block */
130                if (v > s && p > v + 2 && space > p - s) {
131
132                        /* Store var */
133                        if (b->nvars < (int) NELEMS(b->vars) - 1)
134                                b->vars[b->nvars++] = b->buf + b->len;
135
136                        (void) memcpy(b->buf + b->len, "HTTP_", 5);
137                        b->len += 5;
138
139                        /* Copy header name. Substitute '-' to '_' */
140                        n = v - s;
141                        for (i = 0; i < n; i++) {
142                                ch = s[i] == '-' ? '_' : s[i];
143                                b->buf[b->len++] = toupper(ch);
144                        }
145
146                        b->buf[b->len++] = '=';
147
148                        /* Copy header value */
149                        v += 2;
150                        n = p[-1] == '\r' ? (p - v) - 1 : p - v;
151                        for (i = 0; i < n; i++)
152                                b->buf[b->len++] = v[i];
153
154                        /* Null-terminate */
155                        b->buf[b->len++] = '\0';
156                }
157
158                s = p + 1;      /* Shift to the next header */
159        }
160}
161
162static void
163prepare_environment(const struct conn *c, const char *prog,
164                struct env_block *blk)
165{
166        const struct headers    *h = &c->ch;
167        const char              *s;
168        size_t                  len;
169
170        blk->len = blk->nvars = 0;
171
172        /* Prepare the environment block */
173        addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1");
174        addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1");
175        addenv(blk, "%s", "REDIRECT_STATUS=200");       /* PHP */
176        addenv(blk, "SERVER_PORT=%d", c->ctx->port);
177        addenv(blk, "SERVER_NAME=%s", c->ctx->auth_realm);
178        addenv(blk, "SERVER_ROOT=%s", c->ctx->document_root);
179        addenv(blk, "DOCUMENT_ROOT=%s", c->ctx->document_root);
180        addenv(blk, "REQUEST_METHOD=%s", known_http_methods[c->method].ptr);
181        addenv(blk, "REMOTE_ADDR=%s", inet_ntoa(c->sa.u.sin.sin_addr));
182        addenv(blk, "REMOTE_PORT=%hu", ntohs(c->sa.u.sin.sin_port));
183        addenv(blk, "REQUEST_URI=%s", c->uri);
184        addenv(blk, "SCRIPT_NAME=%s", prog + strlen(c->ctx->document_root));
185        addenv(blk, "SCRIPT_FILENAME=%s", prog);        /* PHP */
186        addenv(blk, "PATH_TRANSLATED=%s", prog);
187
188        if (h->ct.v_vec.len > 0)
189                addenv(blk, "CONTENT_TYPE=%.*s",
190                    h->ct.v_vec.len, h->ct.v_vec.ptr);
191
192        if (c->query != NULL)
193                addenv(blk, "QUERY_STRING=%s", c->query);
194
195        if (c->path_info != NULL)
196                addenv(blk, "PATH_INFO=/%s", c->path_info);
197
198        if (h->cl.v_big_int > 0)
199                addenv(blk, "CONTENT_LENGTH=%lu", h->cl.v_big_int);
200
201        if ((s = getenv("PATH")) != NULL)
202                addenv(blk, "PATH=%s", s);
203
204#ifdef _WIN32
205        if ((s = getenv("COMSPEC")) != NULL)
206                addenv(blk, "COMSPEC=%s", s);
207        if ((s = getenv("SYSTEMROOT")) != NULL)
208                addenv(blk, "SYSTEMROOT=%s", s);
209#else
210        if ((s = getenv("LD_LIBRARY_PATH")) != NULL)
211                addenv(blk, "LD_LIBRARY_PATH=%s", s);
212#endif /* _WIN32 */
213
214        if ((s = getenv("PERLLIB")) != NULL)
215                addenv(blk, "PERLLIB=%s", s);
216
217        if (h->user.v_vec.len > 0) {
218                addenv(blk, "REMOTE_USER=%.*s",
219                    h->user.v_vec.len, h->user.v_vec.ptr);
220                addenv(blk, "%s", "AUTH_TYPE=Digest");
221        }
222
223        /* Add user-specified variables */
224        s = c->ctx->cgi_vars;
225        FOR_EACH_WORD_IN_LIST(s, len)
226                addenv(blk, "%.*s", len, s);
227
228        /* Add all headers as HTTP_* variables */
229        add_http_headers_to_env(blk, c->headers,
230            c->rem.headers_len - (c->headers - c->request));
231
232        blk->vars[blk->nvars++] = NULL;
233        blk->buf[blk->len++] = '\0';
234
235        assert(blk->nvars < CGI_ENV_VARS);
236        assert(blk->len > 0);
237        assert(blk->len < (int) sizeof(blk->buf));
238
239        /* Debug stuff to view passed environment */
240        DBG(("%s: %d vars, %d env size", prog, blk->nvars, blk->len));
241        {
242                int i;
243                for (i = 0 ; i < blk->nvars; i++)
244                        DBG(("[%s]", blk->vars[i] ? blk->vars[i] : "null"));
245        }
246}
247
248int
249run_cgi(struct conn *c, const char *prog)
250{
251        struct env_block        blk;
252        char                    dir[FILENAME_MAX], *p;
253        int                     ret, pair[2];
254
255        prepare_environment(c, prog, &blk);
256
257        /* CGI must be executed in its own directory */
258        (void) my_snprintf(dir, sizeof(dir), "%s", prog);
259        for (p = dir + strlen(dir) - 1; p > dir; p--)
260                if (*p == '/') {
261                        *p++ = '\0';
262                        break;
263                }
264       
265        if (my_socketpair(c, pair) != 0) {
266                ret = -1;
267        } else if (spawn_process(c, prog, blk.buf, blk.vars, pair[1], dir)) {
268                ret = -1;
269                (void) closesocket(pair[0]);
270                (void) closesocket(pair[1]);
271        } else {
272                ret = 0;
273                c->loc.chan.sock = pair[0];
274        }
275
276        return (ret);
277}
278
279void
280do_cgi(struct conn *c)
281{
282        DBG(("running CGI: [%s]", c->uri));
283        assert(c->loc.io.size > CGI_REPLY_LEN);
284        memcpy(c->loc.io.buf, CGI_REPLY, CGI_REPLY_LEN);
285        c->loc.io.head = c->loc.io.tail = c->loc.io.total = CGI_REPLY_LEN;
286        c->loc.io_class = &io_cgi;
287        c->loc.flags = FLAG_R;
288        if (c->method == METHOD_POST)
289                c->loc.flags |= FLAG_W;
290}
291
292#endif /* !NO_CGI */
Note: See TracBrowser for help on using the repository browser.