2 * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
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.
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 */
22 * UNIX socketpair() implementation. Why? Because Windows does not have it.
23 * Return 0 on success, -1 on error.
26 my_socketpair(struct conn *c, int sp[2])
28 struct sockaddr_in sa;
30 socklen_t len = sizeof(sa);
32 (void) memset(&sa, 0, sizeof(sa));
33 sa.sin_family = AF_INET;
34 sa.sin_port = htons(0);
35 sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
37 if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
38 elog(E_LOG, c, "mysocketpair: socket(): %d", ERRNO);
39 } else if (bind(sock, (struct sockaddr *) &sa, len) != 0) {
40 elog(E_LOG, c, "mysocketpair: bind(): %d", ERRNO);
41 (void) closesocket(sock);
42 } else if (listen(sock, 1) != 0) {
43 elog(E_LOG, c, "mysocketpair: listen(): %d", ERRNO);
44 (void) closesocket(sock);
45 } else if (getsockname(sock, (struct sockaddr *) &sa, &len) != 0) {
46 elog(E_LOG, c, "mysocketpair: getsockname(): %d", ERRNO);
47 (void) closesocket(sock);
48 } else if ((sp[0] = socket(AF_INET, SOCK_STREAM, 6)) == -1) {
49 elog(E_LOG, c, "mysocketpair: socket(): %d", ERRNO);
50 (void) closesocket(sock);
51 } else if (connect(sp[0], (struct sockaddr *) &sa, len) != 0) {
52 elog(E_LOG, c, "mysocketpair: connect(): %d", ERRNO);
53 (void) closesocket(sock);
54 (void) closesocket(sp[0]);
55 } else if ((sp[1] = accept(sock,(struct sockaddr *) &sa, &len)) == -1) {
56 elog(E_LOG, c, "mysocketpair: accept(): %d", ERRNO);
57 (void) closesocket(sock);
58 (void) closesocket(sp[0]);
62 (void) closesocket(sock);
66 (void) fcntl(sp[0], F_SETFD, FD_CLOEXEC);
67 (void) fcntl(sp[1], F_SETFD, FD_CLOEXEC);
74 addenv(struct env_block *block, const char *fmt, ...)
79 space = sizeof(block->buf) - block->len - 2;
83 n = vsnprintf(block->buf + block->len, space, fmt, ap);
86 if (n > 0 && n < space && block->nvars < CGI_ENV_VARS - 2) {
87 block->vars[block->nvars++] = block->buf + block->len;
88 block->len += n + 1; /* Include \0 terminator */
93 add_http_headers_to_env(struct env_block *b, const char *s, int len)
95 const char *p, *v, *e = s + len;
98 /* Loop through all headers in the request */
101 /* Find where this header ends. Remember where value starts */
102 for (p = s, v = NULL; p < e && *p != '\n'; p++)
103 if (v == NULL && *p == ':')
106 /* 2 null terminators and "HTTP_" */
107 space = (sizeof(b->buf) - b->len) - (2 + 5);
110 /* Copy header if enough space in the environment block */
111 if (v > s && p > v + 2 && space > p - s) {
114 if (b->nvars < (int) NELEMS(b->vars) - 1)
115 b->vars[b->nvars++] = b->buf + b->len;
117 (void) memcpy(b->buf + b->len, "HTTP_", 5);
120 /* Copy header name. Substitute '-' to '_' */
122 for (i = 0; i < n; i++) {
123 ch = s[i] == '-' ? '_' : s[i];
124 b->buf[b->len++] = toupper(ch);
127 b->buf[b->len++] = '=';
129 /* Copy header value */
131 n = p[-1] == '\r' ? (p - v) - 1 : p - v;
132 for (i = 0; i < n; i++)
133 b->buf[b->len++] = v[i];
136 b->buf[b->len++] = '\0';
139 s = p + 1; /* Shift to the next header */
144 prepare_environment(const struct conn *c, const char *prog,
145 struct env_block *blk)
147 const struct headers *h = &c->ch;
148 const char *s, *root = c->ctx->options[OPT_ROOT];
151 blk->len = blk->nvars = 0;
153 /* Prepare the environment block */
154 addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1");
155 addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1");
156 addenv(blk, "%s", "REDIRECT_STATUS=200"); /* PHP */
157 addenv(blk, "SERVER_PORT=%d", c->loc_port);
158 addenv(blk, "SERVER_NAME=%s", c->ctx->options[OPT_AUTH_REALM]);
159 addenv(blk, "SERVER_ROOT=%s", root);
160 addenv(blk, "DOCUMENT_ROOT=%s", root);
161 addenv(blk, "REQUEST_METHOD=%s", known_http_methods[c->method].ptr);
162 addenv(blk, "REMOTE_ADDR=%s", inet_ntoa(c->sa.u.sin.sin_addr));
163 addenv(blk, "REMOTE_PORT=%hu", ntohs(c->sa.u.sin.sin_port));
164 addenv(blk, "REQUEST_URI=%s", c->uri);
165 addenv(blk, "SCRIPT_NAME=%s", prog + strlen(root));
166 addenv(blk, "SCRIPT_FILENAME=%s", prog); /* PHP */
167 addenv(blk, "PATH_TRANSLATED=%s", prog);
169 if (h->ct.v_vec.len > 0)
170 addenv(blk, "CONTENT_TYPE=%.*s",
171 h->ct.v_vec.len, h->ct.v_vec.ptr);
173 if (c->query != NULL)
174 addenv(blk, "QUERY_STRING=%s", c->query);
176 if (c->path_info != NULL)
177 addenv(blk, "PATH_INFO=/%s", c->path_info);
179 if (h->cl.v_big_int > 0)
180 addenv(blk, "CONTENT_LENGTH=%lu", h->cl.v_big_int);
182 if ((s = getenv("PATH")) != NULL)
183 addenv(blk, "PATH=%s", s);
186 if ((s = getenv("COMSPEC")) != NULL)
187 addenv(blk, "COMSPEC=%s", s);
188 if ((s = getenv("SYSTEMROOT")) != NULL)
189 addenv(blk, "SYSTEMROOT=%s", s);
191 if ((s = getenv("LD_LIBRARY_PATH")) != NULL)
192 addenv(blk, "LD_LIBRARY_PATH=%s", s);
195 if ((s = getenv("PERLLIB")) != NULL)
196 addenv(blk, "PERLLIB=%s", s);
198 if (h->user.v_vec.len > 0) {
199 addenv(blk, "REMOTE_USER=%.*s",
200 h->user.v_vec.len, h->user.v_vec.ptr);
201 addenv(blk, "%s", "AUTH_TYPE=Digest");
204 /* Add user-specified variables */
205 s = c->ctx->options[OPT_CGI_ENVIRONMENT];
206 FOR_EACH_WORD_IN_LIST(s, len)
207 addenv(blk, "%.*s", len, s);
209 /* Add all headers as HTTP_* variables */
210 add_http_headers_to_env(blk, c->headers,
211 c->rem.headers_len - (c->headers - c->request));
213 blk->vars[blk->nvars++] = NULL;
214 blk->buf[blk->len++] = '\0';
216 assert(blk->nvars < CGI_ENV_VARS);
217 assert(blk->len > 0);
218 assert(blk->len < (int) sizeof(blk->buf));
220 /* Debug stuff to view passed environment */
221 DBG(("%s: %d vars, %d env size", prog, blk->nvars, blk->len));
224 for (i = 0 ; i < blk->nvars; i++)
225 DBG(("[%s]", blk->vars[i] ? blk->vars[i] : "null"));
230 run_cgi(struct conn *c, const char *prog)
232 struct env_block blk;
233 char dir[FILENAME_MAX], *p;
236 prepare_environment(c, prog, &blk);
237 pair[0] = pair[1] = -1;
239 /* CGI must be executed in its own directory */
240 (void) my_snprintf(dir, sizeof(dir), "%s", prog);
241 for (p = dir + strlen(dir) - 1; p > dir; p--)
247 if (my_socketpair(c, pair) != 0) {
249 } else if (spawn_process(c, prog, blk.buf, blk.vars, pair[1], dir)) {
251 (void) closesocket(pair[0]);
252 (void) closesocket(pair[1]);
255 c->loc.chan.sock = pair[0];
262 do_cgi(struct conn *c)
264 DBG(("running CGI: [%s]", c->uri));
265 assert(c->loc.io.size > CGI_REPLY_LEN);
266 memcpy(c->loc.io.buf, CGI_REPLY, CGI_REPLY_LEN);
267 c->loc.io.head = c->loc.io.tail = c->loc.io.total = CGI_REPLY_LEN;
268 c->loc.io_class = &io_cgi;
269 c->loc.flags = FLAG_R;
270 if (c->method == METHOD_POST)
271 c->loc.flags |= FLAG_W;